/**
 * Cobalt Elements for jQuery
 * base - a collection of base methods and functions.
 *
 * These methods are build on the jQuery and interface foundation to provider
 * client functionality to the Cobalt CMS system.
 *
 * Copyright (c) 2007 Scorpion Design, Inc.
 *
 */
(function($) {

	// Global settings.
	$._DIR =
		{
			_UPPER_RIGHT : { top : true, left: false },
			_UPPER_LEFT : { top : true, left: true },
			_LOWER_RIGHT : { top : false, left: false },
			_LOWER_LEFT : { top : false, left: true},
			_CENTER : { center : true }
		};

	// Record the IE6 browser test.  Note that Vista IE7 has both "MSIE 6.0" and "MSIE 7.0" in the user-agent string.
	$.browser.msie6 = $.browser.msie && /MSIE 6\.0/i.test(window.navigator.userAgent) && !/MSIE 7\.0/i.test(window.navigator.userAgent);

	$.cssNum = function(el, prop) { return parseInt($.css(el.jquery?el[0]:el,prop))||0; };

	// Fire a specific event once all images are loaded.
	$.onImagesLoaded = function(fn,tolerance)
		{
			// Ensure we have a valid function.
			if (!$.isFunction(fn)) return;
			
			var images = $('img').filter(function(el,i) { return !this.complete; });
				
			if (images.length)
			{
				window.$loadedImageCount = images.length;
				window.$imagesLoadedTolerance = $.toInt(tolerance);
				if (!window.$onImagesLoaded) window.$onImagesLoaded = [];
				window.$onImagesLoaded.push(fn);
				images.one('load',$.imageHasLoaded);
			}
			else
				fn();
		},

	// Each time an image is loaded, check to see if we can fire the event.
	$.imageHasLoaded = function()
		{
			// Ensure we still have functions to execute.
			if (!window.$onImagesLoaded) return;

			// Reduce the number of images left.
			try { window.$loadedImageCount--; } catch(ex) {}
			
			// If we've reached 0 or the tolerance, fire the events.
			if ((window.$loadedImageCount||0) <= (window.$imagesLoadedTolerance||0))
			{
				for (var i=0;i<window.$onImagesLoaded.length;i++)
				{
					if ($.isFunction(window.$onImagesLoaded[i]))
						window.$onImagesLoaded[i]();
				}
				window.$onImagesLoaded = null;
			}
		},

	// Obtains the CSS position and size of the element.
	$.fn.position = function(extend)
		{
			if (this.length==0) return {};
			
			// Get the element and initialize the variables.
			var el = this[0];
			var o,w=0,h=0;

			// If a window has been passed, get the scrollbar position to measure the window offset.
			if (el.navigator && el.document)
			{
				var doc = el.document;
				o = {
						left:Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft),
						top:Math.max(doc.documentElement.scrollTop, doc.body.scrollTop)
					};
				w += this.width();
				h += this.height();
			}
			// If the document object has been passed, the offset is 0.
			else if (el.body)
			{
				o = { left : 0, top : 0 };
				w += this.width();
				h += this.height();
			}
			else
			{
				// For the element itself, add the border, padding and margin to get the outer dimensions.
				o = this.offset();
				if (this.is(':visible'))
				{
					w = this[0].offsetWidth;
					h = this[0].offsetHeight;
				}
				else
				{
					// The deep clone is acting wonky with ULs and IE7, so do a shallow clone and copy the innerHTML over.
					var clone = $(this[0].cloneNode(false))
								.html(this.html())
								.css({visibility:'hidden',position:'absolute',display:'block'})
								.appendTo(this[0].parentNode)[0];
					w = clone.offsetWidth;
					h = clone.offsetHeight;
					
					clone.parentNode.removeChild(clone);
				}
			}
			
			return {
					left : o.left,
					top : o.top,
					width : w,
					height : h
				   };
		};

    $.fn.outerHtml = function()
        {
            if (this.length)
            {
                var div = $('<div style="display:none"></div>');
                var clone = $(this[0].cloneNode(false)).html(this.html()).appendTo(div);
                var outer = div.html();
                div.remove();
                return outer;
            }
            else
                return null;
        };

	// Set the left/top CSS position of an element relative to a base object, depending 
	// on which corner to align to, and the direction to fly it.
	//
	// base - the base element that this element will be relative to.  Can optionally be a mouse click event.
	// corner - the starting position, i.e. which corner of the base element to use.
	// direction - which direction should the element fly from the starting position.
	// offset - optionally offset the position {left:0,top:0}
	// bounding - optional position describing the bounding box {left:0,top:0,width:100,height:100}
	$.fn.relativePos = function(base,corner,direction,offset,bounding,isreverse)
		{
			// Fix the Safari client position.
			if ($.browser.safari && typeof base.clientX != 'undefined')
			{
				var doc = document.documentElement, body = document.body;
				base.clientX = base.clientX - (doc && doc.scrollLeft || body && body.scrollLeft || 0) + (doc.clientLeft || 0);
				base.clientY = base.clientY - (doc && doc.scrollTop  || body && body.scrollTop  || 0) + (doc.clientLeft || 0);
			}
			
			// Get the position of the elements (if the base is a click event, get the coordinates of the click).
			if (this.length==0) return {};
			var wn = $(window).position();
			var p1 = base.clientX||base.clientY ? { left:(base.clientX||0)+wn.left,top:(base.clientY||0)+wn.top,width:0,height:0 } : $(base).position();
			var p2 = this.position();
			
			// If we have no width and height, then make the element visible and try it again.
			if (!p2.width && !p2.height)
			{
				this.css({left:-999,top:-999,display:'inline'});
				p2 = { width: this.width(), height: this.height() };
			}
			
			// Get the CSS position of the elements one relative to the other.
			isreverse = isreverse||{};
			var css = $.extend($.boundingPos(p1,p2,bounding||wn,corner,direction,isreverse),{position:'absolute',display:'block'});
			
			// Add any defined offsets.
			if (offset)
			{
				if (isreverse.left) offset.left = 0-(offset.left||0);
				if (isreverse.top) offset.top = 0-(offset.top||0);
				css.left += offset.left||0;
				css.top += offset.top||0;
			}

			// Look for a containing block to adjust the positioning values.
			var parent = this[0].parentNode;
			while (parent && parent!=document && parent!=document.body && !$.browser.safari)
			{
				var pos = $.css(parent,'position');
				if (pos=='absolute' || pos=='relative' || pos=='fixed')
				{
					var p3 = $(parent).offset();
					css.left -= p3.left;
					css.top -= p3.top;
					break;
				}
				
				try { parent = parent.parentNode; }
				catch (e) { parent = null; }
			}

			// Assign the position.
			return this.css(css);
		};

	/**
	 * Take a base position, an element size and a bounding area (usually the viewport), the corner the direction,
	 * and calculate the position the element should be at.  If an element will break the bounding area, try to 
	 * fly it the other direction if there is room.
	 *
	 * @base - The base element position and size.
	 * @el - The element width and height.
	 * @bounding - The bounding box for the element position, defaults to the viewport.
	 * @corner - Which corner to fly off of, using the $.DIR enumeration.
	 * @dir - Which direction to fly, using the $.DIR enumeration.
	 */
	$.boundingPos = function(base,el,bounding,corner,dir,isreverse)
		{
			// Assign defaults.
			if (!bounding) { bounding = $(window).position(); }
			if (!corner) { corner = $._DIR._LOWER_RIGHT; }
			if (!dir) { dir = $._DIR._LOWER_RIGHT; }

			// If we are centering the element.
			if (corner.center)
			{
				var css = {
							left : (base.width-el.width)/2 + base.left,
							top : (base.height-el.height)/2 + base.top
						  };

				if (css.top < bounding.top) css.top = bounding.top;
				if (css.left < bounding.left) css.left = bounding.left;
				
				return css;
			}

			// Get the bounding bottom and right positions.
			bounding.right = bounding.left + bounding.width;
			bounding.bottom = bounding.top + bounding.height;

			// Calculate the left position, and the optional reversed position
			var l =  base.left + (corner.left?0:base.width) - (dir.left?el.width:0);
			var l2 = base.left + (corner.left?base.width:0) - (dir.left?0:el.width);
			
			// Calculate the top position, and the optional reversed position
			var t =  base.top + (corner.top?0:base.height) - (dir.top?el.height:0);
			var t2 = base.top + (corner.top?base.height:0) - (dir.top?0:el.height);

			// If we break the bounding box left or right, see if it fits if the reverse position is used.
			if ((l<bounding.left && l2+el.width<=bounding.right) || (l+el.width>bounding.right && l2>=bounding.left))
			{
				l = l2;
				if (isreverse) isreverse.left = true;
			}

			// If we break the bounding box top or bottom, see if it fits if the reverse position is used.
			if ((t<bounding.top && t2+el.height<=bounding.bottom) || (t+el.height>bounding.bottom && t2>=bounding.top))
			{
				t = t2;
				if (isreverse) isreverse.top = true;
			}

			// We can't fly the element to an impossible position (off the pages upper right).
			if (l<0) { l = 0; }
			if (t<0) { t = 0; }

			return { left : l, top : t };
		};

	// Using a search pattern, returns the current element if it matches, or walks up the tree to the first matching parent.
	$.fn.container = function(pattern)
		{
			if (this.is(pattern))
				return this;
			else if (this.length)
			{
				var p = this[0].stepParent||this[0].parentNode;
				while ( p && !$(p).is(pattern) ) { try { p = p.stepParent||p.parentNode; } catch(error) { p = null; }; }

				if (p)
					return $(p);
				else
					return new jQuery([]);
			}
		};

	// Hover a class 
	$.fn.hoverClass = function(overClass)
		{
			return this.each(function(i)
				{
					this.$self = $(this);
					this.$overClass = overClass;
					this.$self.hover($.overClass,$.outClass);
				});
		};

	// Swap an image on mouseover.
	$.fn.hoverImage = function(swap)
		{
			return this.each(function(i)
				{
					// Get a jQuery representation of this object.
					this.$self = $(this);

					// Get the hover image.
					// If an array is passed, but it isn't long enough, do nothing.
					// If no swap is passed, check for the overImg attribute.
					var img = swap?(swap.push?(swap.length>i?swap[i]:null):swap):this.$self.attr('overImg');
					if (!img) { return; }

					// Assign the hover values.
					this.$overImg = img;
					this.$outImg = this.$self.attr('src');
					this.$self.hover($.overImage,$.outImage);
					
					$.preload(img);
				});
		};

	// Fire a modified hover event, that checks uses the "stepParent" property to determine if we've 
	// moused on/out of an element that should be treated like a parent, but it not a DOM parent.
	//
	// Note that the stepParent property needs to be assigned to the appropriate elements before 
	// this function will work properly.
	$.fn.hoverRelated = function(fnOver, fnOut, child) {

		// A private function for handling mouse 'hovering'
		function handleHover(event) {
			// Check if mouse(over|out) are still within the same parent element
			var parent = event.relatedTarget;
	
			// Traverse up the tree
			while ( parent && parent != this ) try { parent = parent.stepParent||parent.parentNode; } catch(error) { parent = this; };
			
			// If we actually just moused on to a sub-element, ignore it
			if ( parent == this ) return false;

			// Get any stepParent.
			var step = this.stepParent;

			// Check to see if we moused over a stepchild or stepparent.
			var p = event.relatedTarget;
			while (p) { if (p==this || (step&&step==p)) return false; try { p = p.stepParent||p.parentNode; } catch(ex) { p=null }; }

			if (event.type=='mouseout'||event.type=='mouseleave')
			{
				// If we have a stepParent, trigger its mouseout event.
				if (step)
				{
					$(step).trigger('mouseout', [event]);
					return false;
				}

				// Fire the event
				var result = fnOut.apply(this, [event]);

				// Look for a step parent one level higher and fire its mouseout event.
				var step = null;
				p = this.parentNode;
				while ( !step && p ) { try { step = p.stepParent; p = p.parentNode; } catch(ex) { p = null; } }
				if (step) { $(step).trigger('mouseout', [event]); }
				
				return result;
			}
			else
				return fnOver.apply(step||this, [event]);
		}

		// If the handler already has a guid, assign it to the function pointer.
		if (fnOver.guid) { handleHover.guid = fnOver.guid; }

		// Bind the function to the two event listeners
		this.mouseover(handleHover).mouseout(handleHover);

		// If the handler doesn't already have a guid, grab it from the function pointer.
		if (!fnOver.giud) { fnOver.guid = handleHover.guid; }
		if (!fnOut.giud) { fnOut.guid = handleHover.guid; }

		// Set up the step children.
		if (child && child.length && this.length)
		{
			// Get the stepParent value.
			var stepParent = this[0];
			
			// Iterate through each of the stepchildren and assign each one.
			for (var i=0;i<child.length;i++)
			{
				child[i].stepParent = stepParent;
				$(child[i]).mouseover(handleHover).mouseout(handleHover);
			}
		}
	},

	// Assign a context menu event.
	$.fn.contextmenu = function(handler,althandler)
		{
			if (!handler) return this;

			// Create a function pointer that will validate a context-click.
			var fn = function(e)
				{
					// Both the contextmenu and the mousedown events are bound, if the contextmenu event has already
					// fired on this event, there is no need to fire the mousedown event.
					if (e.contextExecuted)
						return;

					// If we are doing a context-click, or if we have a metaKey (CTRL or CMD) held down while clicking.
					if (e.type=='contextmenu'||e.metaKey)
					{
						// Run the function and stop propagation (to prevent the default context menu from appearing).
						if (!handler.apply(this,arguments)==true)
						{
							e.preventDefault();
							e.stopPropagation();
							e.contextExecuted = true;
							return false;
						}
					}
					// Otherwise apply the alt handler.
					else if ($.isFunction(althandler))
					{
						althandler.apply(this,arguments);
					}
				};
			
			// If the handler already has a guid, assign it to the function pointer.
			if (handler.guid) { fn.guid = handler.guid; }
			
			// Bind the event.
			this.bind('contextmenu.cm',fn).bind('mousedown.cm',fn);

			// If the handler doesn't already have a guid, grab it from the function pointer.
			if (!handler.giud) { handler.guid = fn.guid; }
			
			return this;
		},

	// Unbind the context menu event.
	$.fn.uncontextmenu = function(handler)
		{
			return this.unbind('contextmenu.cm').unbind('mousedown.cm');
		},

	// Assign an escape key event.
	$.fn.escape = function(handler,data)
		{
			if (!handler) return this;
			
			// Create a function pointer that will validate the escape key.
			var fn = function(e)
				{
					if (e.which==27)
					{
						return handler.apply(this,arguments);
					}
				};

			// If the handler already has a guid, assign it to the function pointer.
			if (handler.guid) { fn.guid = handler.guid; }
			
			// Bind the event.
			this.bind('keydown',data,fn);
			
			// If the handler doesn't already have a guid, grab it from the function pointer.
			if (!handler.giud) { handler.guid = fn.guid; }
			
			return this;
		},

	// Unbind the escape handler.
	$.fn.unescape = function(handler)
		{
			return this.unbind('keydown',handler);
		},

	// Assign an enter key event.
	$.fn.onenter = function(handler,data)
		{
			if (!handler) return this;
			
			// Create a function pointer that will validate the escape key.
			var fn = function(e)
				{
					if (e.which==13)
					{
						return handler.apply(this,arguments);
					}
				};

			// If the handler already has a guid, assign it to the function pointer.
			if (handler.guid) { fn.guid = handler.guid; }
			
			// Bind the event.
			this.bind('keydown',data,fn);
			
			// If the handler doesn't already have a guid, grab it from the function pointer.
			if (!handler.giud) { handler.guid = fn.guid; }
			
			return this;
		},

	// Unbind the escape handler.
	$.fn.unonenter = function(handler)
		{
			return this.unbind('keydown',handler);
		},

	// Create an floating div that "ghosts" from one position to another.
	$.fn.floatTo = function(dest,css,easing,fn)
		{
			// Check to see if the function is defined earlier in the argument list.
			if (!fn)
			{
				if ($.isFunction(css))
				{
					fn = css;
					css = {};
				}
				if ($.isFunction(easing))
				{
					fn = easing;
					easing = null;
				}
			}

			// Check to see if the easing is defined in the CSS argument.
			if (typeof css == 'string')
			{
				easing = css;
				css = {};
			}
			
			// If we have a source and destination element.
			if (this.length&&dest)
			{
				var p1 = $.extend(css,this.position());
				var p2 = $(dest).position();
				css = $.extend({backgroundColor:'#ffffff',opacity:.5},css);
				css.position = 'absolute';

				var div = $('<div>&nbsp;</div>');
				div.css(css);
				div.appendTo(document.body);

				var complete = $.isFunction(fn) ? function(){div.remove();fn();} : function(){div.remove()};
				div.animate(p2,easing,complete);
			}
		};

	// Show a loading indicator in front of the element.
	$.fn.loading = function(timeout,offset)
		{
			// Default timeout is 10 seconds.  Timeout can be canceled by expressly setting 0 as the parameter.
			if (timeout==null || typeof(timeout)=='undefined') timeout = 10000;

			return this.each(function(i)
				{
					if (!this.$loading)
					{
						this.$failedLoading = false;
						var n = this.nodeName.toLowerCase();
						this.$loading = $('<div class="load_screen"><table cellpadding=0 cellspacing=0 border=0 class="load_table"><tr><td><div class="load_indicator">&nbsp;</div></td><td>&nbsp;Loading...</td></tr></table></div>')
							.css({position:'absolute',zIndex:2000})
							.appendTo(n=='td'||n=='body'?this:this.parentNode)
							.relativePos(this,{center:true},null,offset);

						if (timeout)
						{
							var elem = this;
							this.$loadingTimeout = setTimeout(function()
								{
									if (elem.$loading)
									{
										elem.$loading.remove();
										elem.$loadingTimeout = null;
										elem.$failedLoading = true;
										alert('Timeout -- the process could not be completed.');
									}
								},timeout);
						}
					}
				});
		};

	// Remove the loading indicator.
	$.fn.doneLoading = function()
		{
			return this.each(function(i)
				{
					if (this.$loading)
					{
						this.$loading.remove();
						this.$loading = null;
					}
					if (this.$loadingTimeout)
					{
						clearTimeout(this.$loadingTimeout);
						this.$loadingTimeout = null;
					}
				});
		};

	// Select the end of a control (i.e. move the cursor to the end of its editable area).
	$.fn.selectEnd = function()
		{
			// Get the first element.
			var el = this.length?this[0]:null;

			// Select the end.			
			if (el) { $.selectEnd(el); }			

			return this;
		};

	// Get the first child document inside an iframe.
	$.fn.frameDoc = function()
		{
			// If the jQuery object has an IFRAME in its collection, return a jquery element of the first child document found.
			var doc;
			for (var i=0;i<this.length;i++)
			{
				if (this[i].nodeName)
				{
					switch (this[i].nodeName.toUpperCase())
					{
						case "IFRAME":
						case "FRAME":
							doc = this[i].contentDocument ? this[i].contentDocument : this[i].contentWindow ? this[i].contentWindow.document : null;
							break;
						default:
							if (this[i].body!='undefined') { doc = this[i]; }
							break;
					}
				}
				if (doc) { return new $(doc,doc); }
			}
			return new $();
		};

	// Get the first child document body inside an iframe.
	$.fn.frameBody = function()
		{
			// If the jQuery object has an IFRAME in its collection, return a jquery element of the first child document found.
			var body;
			for (var i=0;i<this.length;i++)
			{
				if (this[i].nodeName)
				{
					switch (this[i].nodeName.toUpperCase())
					{
						case "IFRAME":
						case "FRAME":
							body = this[i].contentDocument ? this[i].contentDocument.body : this[i].contentWindow ? this[i].contentWindow.document.body : null;
							break;
						case "BODY":
							body = this[i];
							break;
						default:
							if (this[i].body!='undefined')
							{
								if (this[i].body==null) { this[i].appendChild(this[i].createElement('BODY')); }
								body = this[i].body;
							}
							break;
					}
				}
				if (body) { return new $(body); }
			}
			return new $();
		};

	// Get the first child document body inside an iframe.
	$.fn.frameAppend = function(a)
		{
			if (!$.browser.msie)
			{
				// For mozilla/opera browers, perform a standard jQuery append.
				this.frameBody().append(a);
			}
			else
			{
				// MSIE doesn't allow node elements to be created in one frame or document and appended to the other.
				// In this case, get the string representation of the elements and append the HTML directly.  Note that this
				// will break any event handling set in code.
				var body = this.frameBody();
				if (body.length>0) { body = body[0]; }
				else { return this; }

				// For each element the supplied collection, add it to the document body of the iframe.
				$.each(arguments,function(i,arg)
					{
						if (!arg) return;
						if (arg.constructor == Number) { arg = arg.toString(); }
						if (arg.outerHTML) { arg = arg.outerHTML; }
						body.innerHTML += arg;
					});
			}
			return this;
		};

	$.extend(
	{
		// Returns a div overlay, which can be faded in to cover the page.
		_pageOverlay : null,
		_overlayFrame : null,
		pageOverlay : function(click)
			{
				if (!$._pageOverlay)
				{
					// If there are any page floaters, the iframe fix below the div will obscure them.
					if ($.pageFloaters().length>0)
					{
						$._overlayFrame = $('<iframe src="javascript:;" style="position:absolute;left:0px;top:0px;border:0;z-index:0;display:none;"></iframe>');
						$._overlayFrame.css({opacity:0});
						$._overlayFrame.appendTo(document.body);
					}
					$._pageOverlay = $('<div id=c_pageOverlay style="position:absolute;z-index:1;top:0px;left:0px;background:#000;border:none;display:none;"></div>');
					$._pageOverlay.css({opacity:0});
					$._pageOverlay.appendTo(document.body);
					if (click) { $._pageOverlay.click(function(e) { $.overlayOut(); }); }
				}
				return $._pageOverlay;
			},

		// Get the size of the overlay.
		overlaySize : function()
			{
				// Get the size of the page and window -- the larger of the two will be used for the overlay size.
				var p1 = $(document.body).position();
				var p2 = $(window).position();
				var l = p1.left<p2.left?p1.left:p2.left;
				var t = p1.top<p2.top?p1.top:p2.top;
				var w = p1.width>p2.width?p1.width:p2.width;
				var h = p1.height>p2.height?p1.height:p2.height;
				return {
							left:l,
							top:t,
							width:w,
							height:h
					   };
			},

		// Fade the in the overlay.
		overlayIn : function(speed,opacity,fn)
			{
				// Add the display block to the page first, as this will affect the document size properties for msie.
				$.pageOverlay().css({display:'block',width:0,height:0});

				var css = $.overlaySize();
				$.pageOverlay().css(css);
				if ($._overlayFrame) { $._overlayFrame.css(css); }
				$.pageOverlay().animate( { opacity:opacity||.6}, speed||400, fn );
				$(window).bind('resize',$.resizeOverlay);
			},

		// Fade out the overlay.
		overlayOut : function(speed,fn)
			{
				var ol = $.pageOverlay();
				ol.animate( { opacity:0}, speed||400, function()
					{
						ol.css({display:'none'});
						if ($._overlayFrame) { $._overlayFrame.css({display:'none'}); }
						if ($.isFunction(fn)) { fn(); }
					});
				$(window).unbind('resize',$.resizeOverlay);
			},

		// Returns an array of "floaters", or elements that will always float on top of a later, such as dropdownlists in msie6.
		// Note that flash can also have this problem, but this can be solved by adding the wmode="transparent" attribute and
		// wrapping the flash object in an element with a z-index:0.  This solution works in IE6, IE7, FF1.5, FF2.0 and Opera.
		// This is a preferred solution than adding flash objects as floaters.
		_pageFloaters : null,
		pageFloaters : function()
			{
				if (jQuery._pageFloaters==null)
				{
					var f = new Array();
					// IE6 will put select objects in front of other layers.
					if (jQuery.browser.msie6) { jQuery('select').each(function() { f.push(this); } ); }
					jQuery._pageFloaters = f;
				}
				return jQuery._pageFloaters;
			},

		// When the window is resized, resize the overlay.
		resizeOverlay : function()
			{
				var css = $.overlaySize();
				$.pageOverlay().css(css);
				if ($._overlayFrame) { $._overlayFrame.css(css); }
			},

		// Popup a url in a thickbox iframe.
		thickbox : function(o)
			{
				// Get the parameters.
				o = o||{};
				var url = o.url;
				var width = o.width||600;
				var height = o.height||400;
				var noclose = o.noclose;

				// Ensure we at least have a url.
				if (!url) return;
				
				// Create the div and pop it up in the middle of the viewport.
				var scroll = (o.noscroll && ' scrolling="no"')||'';
				var div = $('<div id="tb_div" style="display:none;position:absolute;z-index:100"><div class="tb_close" style="text-align:right"><input type="button" value="Close"></div><iframe id="tb_iframe" frameborder="0" src="'+url+'"'+scroll+'></iframe></div>')
					.appendTo(document.body)
					.css({width:width,height:height})
					.relativePos(window,{center:true})
					.css({opacity:0,display:'none'});


				// Get the button.
				var btn = div.find('div.tb_close');
				if (noclose)
					btn.hide();
				else
					btn.find('input:button').click($.closeThickbox);

				// Get the dimensions of the iframe.
				var w = div.innerWidth();
				var h = div.innerHeight() - (o.noclose ? 0 : btn.outerHeight());
				div.find('iframe').css({width:w,height:h});

				// Bring in the overlay and fade in the popup.
				$.overlayIn(function(){ div.css({display:'block'}).fadeTo(300,1);div=null; });
			},

		// Close any thickbox popups.
		closeThickbox : function()
			{
				// Get the thickbox div.
				var div = $('#tb_div');
				
				// Fade the div and the overlay.
				div.fadeTo(300,0,function() { $.overlayOut(); div.remove(); div=null; });
			},

		// Move the cursor to the end of a control.
		selectEnd : function(control)
			{
				if (control.focus) { control.focus(); }
				if (control.createTextRange)
				{
					var range = control.createTextRange();
					range.collapse(false);
					range.select();
				}
				else if (control.setSelectionRange)
				{
					var length = control.value.length;
					control.setSelectionRange(length, length);
				}
			},

		// Resize an element so there is no scrollbar needed.
		autoHeight : function(e)
			{
				var s = this.scrollHeight;
				var o = this.offsetHeight;
				if (s&&s>o)
				{
					if (!this.$self) this.$self = $(this);
					this.$self.css({height:s});
				}
			},

		// Self-contained function to handle the hoverClass methods (preventing unnecessary closures).
		overClass : function(e)
			{
				if (this.$self) { this.$self.addClass(this.$overClass); }
			},

		// Self-contained function to handle the hoverClass methods (preventing unnecessary closures).
		outClass : function(e)
			{
				if (this.$self) { this.$self.removeClass(this.$overClass); }
			},

		// Self-contained function to handle the overImage methods (preventing unnecessary closures).
		overImage : function(e)
			{
				if (this.$self) { this.$self.attr('src',this.$overImg); }
			},

		// Self-contained function to handle the outImage methods (preventing unnecessary closures).
		outImage : function(e)
			{
				if (this.$self) { this.$self.attr('src',this.$outImg); }
			},

		// Add a hidden image to the page with the supplied url to preload the image.
		preload : function(img)
			{
				// Get the image preloader.
				var preloader = $('#hidden_preloader');
				if (!preloader.length) { preloader = $('<div id="hidden_preloader" style="display:none;"></div>').appendTo(document.body); }
				
				// Add the image to the preloader if it isn't already there.
				if (preloader.find('img[src='+img+']').length==0)
					preloader.append('<img src="'+img+'">');
			},

		aposRegex : new RegExp("'","g"),
		quotRegex : new RegExp('"','g'),

		// Encode a string for safe storage in a text field, as well as save for form/querystring submission to the server.
		encode : function(val)
			{
				if (val==null)
					return '';
				else
					return encodeURIComponent(val+"").replace($.aposRegex,'%27').replace($.quotRegex,'%22');
			},

		// Decode an encoded string.
		decode : function(val)
			{
				return val?decodeURIComponent(val):'';
			},

		// Return a text value, parsed as an integer.  Return any NaN as 0.
		toInt : function(amount)
			{
				if (!amount) return 0;
				if (typeof(amount)=='string')
				{
					// Format the number, removing any leading zeroes so it doesn't get treated as an octal.
					amount = $.trimLeft(amount.replace(/[^\d.-]/g,''),'0');
				}
				var val = parseInt(amount);
				if (isNaN(val)) { return 0; }
				else { return val; }
			},

		// Trim from the left side of a string.
		trimLeft : function(value,character)
			{
				// Ensure we have a value, converting to a string as necessary.
				if (!value) return '';
				value = value + '';

				// Trim the character (defaulting to a space).
				while (value.charAt(0) == (character||' ')) {
					value = value.substring(1);
				}
				return value;
			},

		// Return a text value, parsed as a float.  Return any NaN as 0.
		toFloat : function(amount)
			{
				if (!amount) return 0.00;
				if (typeof(amount)=='string') { amount = amount.replace(/[^\d.-]/g,''); }
				var val = parseFloat(amount);
				if (isNaN(val)) { return 0.00; }
				else { return val; }
			},

		// Return a value, parsed as a boolean.
		toBool : function(val)
			{
				// If no value was supplied, return false.
				if (typeof(val)=='undefined') return false;
				if (!val) return false;
				
				// If we already have a boolean value, return it.
				if (val==true) return true;
				if (val==false) return false;
				
				// Conver the value to a string and check for a match on a "TRUE" type value.
				switch ((''+val).toUpperCase())
				{
					case "1":
					case "ON":
					case "TRUE":
					case "YES":
					case "SUCCESS":
						return true;
					default:
						return false;
				}
			},

		// Cast a value as a 2-digit currency with comma-seperators.
		currencyFormat : function(amount)
			{
				// Parse the value as a float.
				var i = $.toFloat(amount);
				if(isNaN(i)) { i = 0.00; }

				// Get any minus sign.
				var minus = '';
				if(i < 0) { minus = '-'; }

				// Get both sides of the decimal.
				i = Math.abs(i);
				i = parseInt((i + .005) * 100);
				i = i / 100;

				// Build the string.
				s = new String(i);
				if(s.indexOf('.') < 0) { s += '.00'; }
				if(s.indexOf('.') == (s.length - 2)) { s += '0'; }
				s = minus + s;

				var delimiter = ",";
				var a = s.split('.',2);
				var d = a[1];
				var i = parseInt(a[0]);
				if(isNaN(i)) { return ''; }
				var minus = '';
				if(i < 0) { minus = '-'; }
				i = Math.abs(i);
				var n = new String(i);
				var a = [];
				while(n.length > 3)
				{
					var nn = n.substr(n.length-3);
					a.unshift(nn);
					n = n.substr(0,n.length-3);
				}
				if(n.length > 0) { a.unshift(n); }
				n = a.join(delimiter);
				if(d.length < 1) { amount = n; }
				else { amount = n + '.' + d; }
				amount = minus + amount;
				return '$'+amount;

			}
	});

})(jQuery);
