/// <reference path="jquery.intellisense.js"/>  
/**
 * 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);
	$.browser.msie9 = $.browser.msie && $.browser.version == 9;

	// 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 > tolerance)
		{
			window.$loadedImageCount = images.length;
			window.$imagesLoadedTolerance = $.toInt(tolerance);
			if (!window.$onImagesLoaded) window.$onImagesLoaded = [];
			window.$onImagesLoaded.push(fn);
			images.one('load',$.imageHasLoaded);

			// Set a catch-all timeout to fire the events after 20 seconds even if all the images aren't loaded.
			setTimeout($.fireImagesLoaded,20000);
		}
		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))
		{
			$.fireImagesLoaded();
		}
	};

	// The images have loaded.
	$.fireImagesLoaded = function() {
		// Ensure we still have functions to execute.
		if (!window.$onImagesLoaded) return;

		for (var i=0;i<window.$onImagesLoaded.length;i++)
		{
			if ($.isFunction(window.$onImagesLoaded[i]))
				window.$onImagesLoaded[i]();
		}
		window.$onImagesLoaded = null;
	};

	// Parse the url as a function or as a string with a default property
	$.getAjaxUrl = function(url, params) {
		if (!url) {
			return '';
		} else if ( $.isFunction(url) ) {
			var args = Array.prototype.slice.call(arguments);
			args.shift();
			return url.apply(this, args);
		} else {
			// Remove any named anchors in the link.
			var url = url.split('#').shift();

			// If we have parameters, add them to the querystring portion of the url.
			if (params) {
				url = url.split('?');
				var query = {};
				if (url.length>1) {
					// Build the querystring into a dictionary.
					var parts = url[1].split('&');
					for (var i=0;i<parts.length;i++) {
						var pair = parts[i].split('=');
						query[pair[0]] = pair.length?pair[1]:'';
					}
				}

				// Extend the supplied parameters, ignoring case.
				for (var p1 in params) {
					var existing = false;
					for (var p2 in query) {
						if (p1.toLowerCase()==p2.toLowerCase()) {
							query[p2] = params[p1] == null ? null : $.encode(params[p1]);
							existing = true;
							break;
						}
					}
					if (!existing) {
						query[p1] = params[p1] == null ? null : $.encode(params[p1]);
					}
				}

				// Convert the query dictionary to a string.					
				var sb = [];
				for (var p in query) {
					var val = query[p];
					if ( val === null || val === '' || val === undefined ) {
						continue;
					} else {
						sb.push(p+'='+val);
					}
				}

				// Assign the completed url.					
				url = url[0]+'?'+sb.join('&');
			}
			return url;
		}
	};

	// Get the outer html of an element.
    $.fn.outerHtml = function() {
        if ( this.length ) {
			if (this[0].outerHTML) {
				return $.trim( this[0].outerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g, "") );
			} else {
				var div = document.createElement('div'),
					clone = this[0].cloneNode(false),
					html = this[0].innerHTML;
				div.appendChild( clone );
				if ( html ) {
					clone.innerHTML = html;
				}
				return $(div).html();
            }
        } else {
            return null;
		}
    };

	// Hover a class 
	$.fn.hoverClass = function(overClass) {
		return this.each(function(i){this._overclass=overClass;}).hover($.overClass,$.outClass);
	};
	$.overClass = function(e) { if (this._overclass) { $(this).addClass(this._overclass); } };
	$.outClass	= function(e) { if (this._overclass) { $(this).removeClass(this._overclass); } };

	// Blank a value on focus.
	$.fn.toggleValue = function() {
		return this.each(function(i){
			var el = $(this);
			var orig = el.attr('_origvalue');
			if ( orig !== undefined ) {
				this.origValue = orig;
			} else {
				this.origValue = this.value;
			}
		}).bind('focus',$.clearValue).bind('blur',$.restoreValue);
	};
	$.clearValue = function(e) {
		if ( this.origValue === undefined ) {
			this.origValue = this.value;
		}
		// If we have an original value and it matches the current value, blank it out.
		if ( this.origValue && this.origValue === this.value ) {
			this.value = '';
		}
	};
	$.restoreValue = function(e) {
		// If the current value is blank and we have an original value, re-assign it.
		if (typeof this.value !='undefined' && !this.value && this.origValue)
			this.value = this.origValue;
	};

	// 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');
			var cls = this.$self.attr('_overclass');
			if (!img && !cls) { return; }

			// If we have a "sticky" hover image.
			var href = null;
			var root = null;
			if ( $.toBool(this.$self.attr('_sticky')) && !window._noflash ) {
				// Get the path to the parent anchor.
				var base = $(document.body).attr('_base');
				var top = $.toBool(this.$self.attr('_top'));
				var link = this.$self.closest('a');
				if ( !link.length ) {
					link = this.$self.children('a');
				}
				href = ( link.attr('href') || "" ).split('?').shift().toLowerCase();
				root = window.location.href.substring(base.length).split('?').shift().toLowerCase();

				// Clean the link href.
				if ( href ) {
					if ( href.startsWith(base) ) {
						href = href.substring(base.length);
					} else if ( href.startsWith('/') ) {
						href = href.substring(1);
					}
				}

				// Clean the root href.
				switch ( root ) {
					case 'index.aspx':
					case 'home.aspx':
						root = "";
						break;
				}
			}

			// If the href matches the current page url.
			if ( href != null && ( href == root || ( !top && href && root.startsWith(href.split('.').shift()+'/') ) ) ) {
				// Is this a CSS based image swap?
				if ( cls ) {
					this.$self.addClass( cls );
					return;
				} else {
					this.$self.attr('src',img);
				}
			} else {
				if ( cls ) {
					this.$self.hoverClass( cls );
				} else {
					// Assign the hover values.
					this._outimg = this.$self.attr('src');
					this._overimg = img;
					this.$self.hover($.overImage,$.outImage);
					$.preload(img);
				}
			}
		});
	};
	$.overImage = function(e) { if (this._overimg) { $(this).attr('src',this._overimg); } };
	$.outImage	= function(e) { if (this._outimg)  { $(this).attr('src',this._outimg); } };

	// Alert the user using a self-destroying dialog box.
	$.alert = function(title, message, options) {
		if ( title && message === undefined ) {
			message = title;
			title = 'Alert!';
		}

		if ( options && typeof options === 'number' ) {
			options = {width:options};
		}

		if ( message && message.indexOf('<') < 0 && $.cms.page && Math.random() < 0.05 && $.cms.page.PPM(1) ) {
			var img = ( Math.round(Math.random()*100000) % 4 ) + 1;
			var biff;
			switch ( Math.round(Math.random()*100000) % 8 ) {
				case 0:
					biff = "That's about as funny as a screen door on a battleship.";
					break;
				case 1:
					biff = "Must be rough bein' named after a complete butthead.";
					break;
				case 2:
					biff = "Hello? Hello? Anybody home?";
					break;
				case 3:
					biff = "I don't know what liquor smells like, cuz I'm too young to drink it.";
					break;
				case 4:
					biff = "Noon? I do my killin' before breakfast! Seven o'clock!";
					break;
				case 5:
					biff = "I'll hunt you and shoot you down like a duck.";
					break;
				case 6:
					biff = "Oh, McFly, your shoe's untied.";
					break;
				case 7:
					biff = "What are you looking at butthead?";
					break;
				default:
					biff = "Manure! I hate manure!";
					break;
			}
			message = [ "<img style='float:right;width:120px;height:156px;margin:0px 0px 10px 15px' src='/Admin/images2/wizard/biff", img, ".jpg'>", message, "<br><br><em>", biff, "</em><div style='clear:both;height:1px'></div>" ].join("");
			if ( !options ) {
				options = { width: 350 };
			} else if ( !options.width || options.width > 350 ) {
				options.width = 350;
			}

			if ( options.buttons && options.buttons.Yes && options.buttons.No ) {
				var buttons = {
					"Marty": options.buttons.Yes,
					"Biff": options.buttons.No
				};
				delete options.buttons;
				options.buttons = buttons;
			}
		}

		var $el = $('<div class="ui-alert"></div>')
			.dialog($.extend({
				title: title||'',
				closeOnEscape: true,
				selectOnEnter: true,
				width: 'auto',
				minHeight: 75,
				modal: true,
				buttons: {
					"OK" : function(e, widget) {
						widget.close();
					}
				},
				open: function(e, data, widget) {
					widget.uiDialog.find("div.ui-dialog-buttonpane a:first").focus().blur();
				},
				close: function(e, data, widget){
					// Prevent any bubbling (in case the user pressed "enter" on the close button).
					if ( e && e.preventDefault ) {
						e.preventDefault();
						e.stopPropagation();
						e.stopImmediatePropagation();
					}
					// Fire any onclose event.
					if ( options && $.isFunction(options.onclose) ) {
						options.onclose();
					}
					$(this).remove();
					return false;
				}
			},options||{}));

		// Set a named width on the dialog box once the title has been created.
		if ( message && ( !options || !options.width ) ) {
			var box = $el.dialog('widget');
			var w = 300;
			if ( title ) {
				var bar = box.children('div.ui-dialog-titlebar');
				w = 0;
				bar.children().each(function(i){
					w += $(this).outerWidth(true);
				});
				w += ( $.toInt(bar.css('paddingLeft')) + $.toInt(bar.css('paddingRight')) );
			}
			box.css({width:Math.max(w,300)});
		}
		$el.html(message||'');

		// Center the alert with a minor offset.
		$el.dialog('widget').place({
			my: 'center',
			at: 'center',
			of: window,
			collision: 'fit'
		});

		return $el;
	};

	// Confirm an action before continuing.
	$.confirm = function(title, message, options, yes, no) {
		options = $.extend({
			closeText:'',
			buttons:{}
		},options||{});

		// Add the Yes button.
		options.buttons[ yes || "Yes" ] = function(e, widget) {
				if ( $.isFunction(widget.options.onconfirm) ) {
					widget.options.onconfirm.apply(widget, [e,true]);
				}
				return widget.close(e);
			};

		// And the No button.
		options.buttons[ no || "No" ] = function(e, widget) {
				if ( $.isFunction(widget.options.oncancel) ) {
					widget.options.oncancel.apply(widget, [e,false]);
				}
				return widget.close(e);
			};

		return $.alert(title,message,options);
	};

	$.fn.handleConfirm = function(yes,no,cls) {
		return this.each(function(i){
			var link = $(this);
			var m = /return confirm\(['"](.+?)["']\);?/.exec( ""+link.attr('onclick') );
			var h = /^javascript:(.+)/.exec( link.attr('href') );
			var message = m && m[1];
			var href = h && h[1];
			if ( message && href ) {
				link.attr('href', 'javascript:void(0);');
				link.removeAttr('onclick');
				link.unbind('click').click(function(e){
					var fn = function(_href,_action){
						return function(){
							if ( _action ) {
								_action(_href);
							} else {
								$(document.body).loading({message:null});
								eval(_href);
							}
							_href = null;
							_action = null;
						}
					}(href,link.data('action'));
					// If this buttons triggers .NET form valdation, and the validation fails, don't bother with the confirm popup.
					var m = /new WebForm_PostBackOptions\([^\)]+\)/.exec(href),
						options = m && eval(m[0]);
					if ( options && options.validation && typeof(Page_ClientValidate) == 'function' && !Page_ClientValidate(options.validationGroup) ) {
						eval(href);
						return false;
					}
					$.confirm('CONFIRM', message, {onconfirm:fn,dialogClass:cls}, yes, no);
					e.preventDefault();
					e.stopPropagation();
					e.stopImmediatePropagation();
					return false;
				});
			}
		});
	};

	// Return a keypress object that can be used in comparison to key events.
	$.getKey = function(e) {
		if ( !e ) {
			return null;
		} else if ( e === true ) {
			return true;
		} else if (typeof e === "number") {
			return {which:e,ctrlKey:false,altKey:false,shiftKey:false};
		} else if (typeof e == "string") {
			if ( e === 'ALL' ) {
				return true;
			}
			// Get the parts of the key string.
			var parts = e.split('-');
			var key = parts.pop();

			// Look up the last code, or get its ascii.
			var which = key === 'ENTER' ? 13 : $.ui.keyCode[key];
			if (!which && key.length==1)
				which = key.charCodeAt(0);

			// If we don't have a keycode, the source data is invalid.
			if (!which)
				return null;
			else
				e = {which:which,ctrlKey:false,altKey:false,shiftKey:false};

			// Get any meta keypresses.
			while (key=parts.pop()) {
				switch (key)
				{
					case 'CTRL':
						e.ctrlKey = true;
						break;
					case 'ALT':
						e.altKey = true;
						break;
					case 'SHIFT':
						e.shiftKey = true;
						break;
					default:
						// Illegal value.
						return null;
				}
			}
			return e;
		} else if (e.which) {
			// Return a subset of the keypress event.
			return {
				which:e.which,
				ctrlKey:e.ctrlKey?true:false,
				altKey:e.altKey?true:false,
				shiftKey:e.shiftKey?true:false
			};
		} else {
			return null;
		}
	};

	// When binding keydowns to an item, use a single event with an array of possible keypresses.
	$.handleBoundKey = function(e) {
		// Clone the handlers to prevent manipulation.
		var handlers = e.data.slice(0);

		// Iterate through the bound handlers.
		var result = null;
		for (var j=0; j<handlers.length; j++) {
			var h = handlers[j];

			// If the keypress matches.
			if (h.key === true ||
				( e.which === h.key.which && 
				  e.ctrlKey === h.key.ctrlKey && 
				  e.altKey === h.key.altKey && 
				  e.shiftKey === h.key.shiftKey ) ) {
				// Fire the handler, note if we have a result of false.
				if ( h.handler.apply(this, arguments) === false ) {
					result = false;
				}

				// If immediate propagation has been stopped, exit now.
				if ( e.isImmediatePropagationStopped() ) {
					break;
				}
			}
		}
		return result;
	};

	// A shortshut function that binds a keydown handler for a specific keystroke.
	$.fn.bindkey = function(which, handler,priority) {
		var key = $.getKey(which);
		if ( key && handler ) {
			// Ensure a unique guid for the handler so it can be removed.
			if ( !handler.guid ) {
				handler.guid = jQuery.guid++;
			}
			var item = {
				key:key,
				handler:handler,
				priority:!!priority
			};

			// Iterate through the elements in the jquery collection.
			for (var i=0; i<this.length; i++) {
				// Check to see if the bindkey event has already been assigned.
				var elemData = (jQuery._data||jQuery.data)( this[i] );
				var events = elemData && elemData.events;
				var handlers = events && events['keydown'];

				// Look for a matching handler.
				var fn = null;
				if ( handlers ) {
					for (var j=0; j<handlers.length; j++) {
						if ( handlers[j].namespace === 'bindkey' ) {
							fn = handlers[j];
							break;
						}
					}
				}

				if ( fn ) {
					if ( !priority ) {
						// Add this handler just after the last priority item.
						for ( var j=0; j<fn.data.length; j++ ) {
							if ( !fn.data[j].priority ) {
								fn.data.splice(j,0,item);
								item = null;
								break;
							}
						}
						// If we don't have any non-priority items, add this handler at the end.
						if ( item ) {
							fn.data.push(item);
						}
					} else {
						// Add the new item to the list of keystrokes, at the beginning of the list.
						fn.data.unshift(item);
					}
				} else {
					// If we don't have the function, create it.
					fn = function(e){
						$.handleBoundKey.apply(this, arguments);
					};

					// Bind the event.
					this.eq(i).bind('keydown.bindkey', [item], fn);
				}
			}
		}
		return this;
	};

	// Unbind the specific handler.
	$.fn.unbindkey = function(which, handler) {
		var key = $.getKey(which);
		if ( key && handler && handler.guid ) {
			for (var i=0; i<this.length; i++) {
				// Check to see if the bindkey event has already been assigned.
				var elemData = (jQuery._data||jQuery.data)( this[i] );
				var events = elemData && elemData.events;
				var handlers = events && events['keydown'];

				// Look for a matching handler.
				var fn = null;
				if ( handlers ) {
					for (var j=0; j<handlers.length; j++) {
						if ( handlers[j].namespace === 'bindkey' ) {
							fn = handlers[j];
							break;
						}
					}
				}

				// If we have the bindkey event.
				if ( fn && fn.data ) {
					// Iterate through the keystrokes backwards.
					for (var j=fn.data.length-1; j>=0; j--) {
						var item = fn.data[j];

						// Make sure we have a match.
						if ( handler.guid !== item.handler.guid ||
							 (key === true && item.key !== true ) ||
							 (key !== true && item.key === true ) ||
							 key.which !== item.key.which ||
							 key.ctrlKey !== item.key.ctrlKey ||
							 key.shiftKey !== item.key.shiftKey ) {
							continue;
						}

						// Remove the handler from the array.
						if ( j === fn.data.length-1 ) {
							fn.data.pop();
						} else if ( j === 0 ) {
							fn.data.shift();
						} else {
							fn.data.splice(j, 1);
						}
					}

					// If we removed all keystrokes, unbind the event completely.
					if ( fn.data.length === 0 ) {
						this.unbind('keydown.bindkey');
					}
				}
			}
		} else if ( which === undefined ) {
			this.unbind('keydown.bindkey');
		}
		return this;
	};

	// If multiple keydown events are added to the same element, they need to be added in reverse order, so that the end 
	// up being called in reverse sequence.  This is used primarly with the ESCAPE key applied to the DOCUMENT element, 
	// so that later-added keydown events can stopImmediatePropation on earlier-added events.
	$.fn.flipevent = function( event, handler ) {
		var name = event.split('.').shift();
		var guid = handler.guid;

		return this.each(function(i){
			var elemData = (jQuery._data||jQuery.data)( this );
			var events = elemData && elemData.events;
			var handlers = events && events[name];
			if ( handlers && 
				 handlers.length > 1 && 
				 handlers[handlers.length-1].guid === guid ) {
				// Move the handler from the end to the beginning.
				handlers.unshift( handlers.pop() );
			}
		});
	};

	// Get the absolute offset and size of the object.
	$.fn.dimensions = function() {
		if (!this.length) return null;
		var elem = this[0];
		var o;
		if ("scrollTo" in elem && elem.document) {
			var doc = elem.document;
			o = {
				left: Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft),
				top: Math.max(doc.documentElement.scrollTop, doc.body.scrollTop),
				width: this.width(),
				height: this.height()
			};
		} else if (elem.body) {
			o = { 
				left: 0,
				top: 0,
				width: this.width(),
				height: this.height()
			};
		} else if (elem.nodeName && elem.nodeName.toLowerCase() === 'body') {
			var doc = elem.ownerDocument;
			o = { 
				left: 0,
				top: 0,
				width: this.width() + 
						$.toInt(this.css('marginLeft')) + 
						$.toInt(this.css('marginRight')),
				height: Math.max(
					this.height() + 
						$.toInt(this.css('paddingTop')) +
						$.toInt(this.css('paddingBottom')), 
					$(doc.defaultView||doc.parentWindow).height())
			};
		} else if (elem.nodeName && elem.nodeName.toLowerCase() === 'tr' && elem.childNodes.length ) {
			// If we have a table row, use the dimensions of the first and last cell instead.
			var first = elem.firstChild;
			while ( first && first.nodeType !== 1 && first.nextSibling ) {
				first = first.nextSibling;
			}
			if ( !first || first.nodeType !== 1 ) {
				// If we don't have any child nodes, try the TR instead, even though it will be innacurate.
				var o = $(elem).offset();
				o.width = elem.offsetWidth || elem.scrollWidth;
				o.height = elem.offsetHeight || elem.scrollHeight;
			} else {
				// Get the last sibling as well.
				var last = elem.lastChild;
				while ( last && last.nodeType !== 1 && last.previousSibling ) {
					last = last.previousSibling;
				}
				if ( !last || last.nodeType !== 1 ) {
					last = first;
				}
				// Get the positions.
				o = $(first).offset();
				var left = last.offsetLeft - first.offsetLeft;
				o.width = left + ( last.offsetWidth || last.scrollWidth );
				o.height = Math.max( first.offsetHeight, last.offsetHeight ) || Math.max( first.scrollHeight, last.scrollHeight );
			}
		} else {
			var w,h;
			if ( elem.nodeName.toLowerCase() === 'a' && elem.childNodes.length === 1 && elem.childNodes[0].nodeName.toLowerCase() === 'img' ) {
				o = $(elem.childNodes[0]).offset();
				w = Math.max( elem.offsetWidth, elem.childNodes[0].offsetWidth );
				h = Math.max( elem.offsetHeight, elem.childNodes[0].offsetHeight );
			} else {
				o = this.offset();
				w = elem.offsetWidth;
				h = elem.offsetHeight;
			}
			if ( w + h === 0 ) {
				w = elem.scrollWidth;
				h = elem.scrollHeight;
			}
			o.width = w;
			o.height = h;
		}
		return o;
	};

	// An alternative to the jquery.ui.position script, which is not totally operational.
	$.fn.place = function(options) {
		return this.each(function(i){
			// Get the element and record the current style attribute.
			$el = $(this);
			var style = $el.attr('style');

			// Ensure the element can be positioned.
			var css = { display: 'block' };
			if ( /static|relative/.test($el.css('position')) ) {
				css.position = 'absolute';
			}

			// Set the element for block display so it is visible.
			$el.css(css);

			// Get the placement css.
			css = $.placement($el, options);

			// If we have CSS, set it.
			if (css) {
				$el.css(css);
			} else {
				// Otherwise, revert the named style.
				if (style) {
					$el.attr('style',style);
				} else {
					$el.removeAttr('style');
				}
			}
		});
	};

	// Is this element a parent of another?
	$.fn.isParent = function(of) {
		if (!of) {
			return false;
		}

		// Get the elements to compare.
		var parent = this.length ? this[0] : null;
		var p = of.jquery ? (of.length ? of[0].parentNode : null) : of.parentNode;

		// Look for a matching parent.
		while (p) {
			if ( p === parent ) {
				return true;
			} else {
				try { p = p.parentNode; }
				catch(ex){ p = null; }
			}
		}

		// Didn't find one.
		return false;
	};

	// Search for a parent element with an overflow set.
	$.fn.overflowParent = function() {
		var p = this.length ? this[0].parentNode : null,
			_r1 = /^(?:body|html)/i,
			_r2 = /hidden|scroll|auto/i;

		while (p) {
			if ( !p.nodeName || _r1.test(p.nodeName) ) {
				return null;
			} else if ( _r2.test( $.curCSS(p,'overflow') + $.curCSS(p,'overflowX') + $.curCSS(p,'overflowY') ) ) {
				return $(p);
			}
			p = p.parentNode;
		}
		return null;
	};

	// Calculate the css placement of an object in relation to another.
	$.placement = function($el, options) {
		// validate the inputs.
		if (!options || !options.of) return null;
		var of = $(options.of);
		if (!of.length) return null;

		// Get the positions of everything.
		var dim1 = $el.dimensions();
		var dim2 = of.dimensions();
		var op = $el.offsetParent();
		var parent, scroll;
		if ( op.is('body,html') ) {
			parent = { left: 0, top: 0 };
			scroll = { left: 0, top: 0 };
		} else {
			parent = op.offset();
			scroll = { left: op[0].scrollLeft || 0, top: op[0].scrollTop || 0 };
		}
		var offset = options.offset||{};

		// Adjust for any requested margin.
		var margin = options.margin||{};
		dim1.height += ( ( margin.top || 0 ) + ( margin.bottom || 0 ) );
		dim1.width += ( ( margin.left || 0 ) + ( margin.right || 0 ) );

		var border;
		if ($el[0].parentNode === of[0]) {
			// If the placed element is a child of the "of" element, we'll need to adjust the position for it's border.
			border = {
				top: $.toInt(of.css('borderTopWidth')),
				right: $.toInt(of.css('borderRightWidth')),
				bottom: $.toInt(of.css('borderBottomWidth')),
				left: $.toInt(of.css('borderLeftWidth'))
			};
		} else if ( op.isParent(of) ) {
			// If the placed element and the positioned element share the same offset parent.
			border = {
				top: $.toInt(op.css('borderTopWidth')),
				right: $.toInt(op.css('borderRightWidth')),
				bottom: $.toInt(op.css('borderBottomWidth')),
				left: $.toInt(op.css('borderLeftWidth'))
			};
		} else {
			border = { top: 0, right: 0, bottom: 0, left: 0};
		}

		// By default, the element will be flown out to the right, fitting inside the window.
		var my = (options.my||'left top').split(' ');
		var at = (options.at||'right top').split(' ');
		var collision = (options.collision||'fit fit').split(' ');
		if ( my.length === 1 && my[0] === 'center' ) my[1] = my[0];
		if ( at.length === 1 && at[0] === 'center' ) at[1] = at[0];
		if ( collision.length === 1 ) collision[1] = collision[0];

		// Calculate the position.
		var css = { left: 0, top: 0};
		switch (my[0]) {
			case 'center':
				css.left -= parseInt(dim1.width/2);
				break;
			case 'right':
				css.left -= dim1.width;
				break;
		}
		switch (my[1]) {
			case 'center':
				css.top -= parseInt(dim1.height/2);
				break;
			case 'bottom':
				css.top -= dim1.height;
				break;
		}
		switch (at[0]) {
			case 'center':
				css.left += parseInt(dim2.width/2);
				break;
			case 'right':
				css.left += dim2.width - border.right;
				break;
			case 'left':
				css.left -= border.left;
				break;
		}
		switch (at[1]) {
			case 'center':
				css.top += parseInt(dim2.height/2);
				break;
			case 'bottom':
				css.top += dim2.height - border.bottom;
				break;
			default:
				css.top -= border.top;
				break;
		}

		// Adjust the position as needed.
		css.left += ( dim2.left - parent.left + ( offset.left || 0 ) + scroll.left );
		css.top +=  ( dim2.top  - parent.top  + ( offset.top  || 0 ) + scroll.top );

		// See if a custom container has been defined.
		var container;
		if ( options.containment === 'parent' ) {
			container = op;
		} else if ( options.containment ) {
			if ( options.containment.jquery ) {
				container = options.containment;
			} else if ( typeof options.containment === 'string' ) {
				container = $el.closest( options.containment );
			}
		}

		if ( container ) {
			// Get the container dimensions, adjusting for any scrollbars.
			wn = container.offset();
			wn.width = container[0].clientWidth;
			wn.height = container[0].clientHeight;
		} else {
			wn = $(window).dimensions();
		}

		var over, under;
		switch (collision[0]) {
			case 'fit':
				over = css.left + dim1.width + parent.left - wn.left - wn.width;
				if ( options.containment ) {
					css.left = css.left - Math.max( 0, over);
				} else {
					css.left = over > 0 ? Math.max( 0, css.left-over ) : Math.max( 0, css.left );
				}
				break;
			case 'flip':
				over = css.left + dim1.width + parent.left - wn.left - wn.width;
				under = css.left + parent.left - wn.left;
				if ( under < 0 ) {
					if ( my[0] === 'right' ) {
						css.left += dim1.width;
						switch ( at[0] ) {
							case 'left':
								css.left += dim2.width;
								break;
							case 'right':
								css.left -= dim2.width;
								break;
						}
					}
				} else if ( over > 0 ) {
					if ( my[0] === 'left' ) {
						css.left -= dim1.width;
						switch ( at[0] ) {
							case 'right':
								css.left -= dim2.width;
								break;
							case 'left':
								css.left += dim2.width;
								break;
						}
					}
				}
				break;
				
		}
		switch (collision[1]) {
			case 'fit':
				over = css.top + dim1.height + parent.top - wn.top - wn.height;
				css.top = over > 0 ? Math.max( 0, css.top-over ) : Math.max( 0, css.top );
				break;
			case 'flip':
				over = css.top + dim1.height + parent.top - wn.top - wn.height;
				under = css.top + parent.top - wn.top;
				if ( under < 0 ) {
					if ( my[1] === 'bottom' ) {
						css.top += dim1.height;
						switch ( at[1] ) {
							case 'top':
								css.top += dim2.height;
								break;
							case 'bottom':
								css.top -= dim2.height;
								break;
						}
					}
				} else if ( over > 0 ) {
					if ( my[1] === 'top' ) {
						css.top -= dim1.height;
						switch ( at[1] ) {
							case 'bottom':
								css.top -= dim2.height;
								break;
							case 'top':
								css.top += dim2.height;
								break;
						}
					}
				}
				break;
		}

		css.left += ( margin.left || 0 );
		css.top += ( margin.top || 0 );

		// Fixed elements are relative to the viewport, so adjust for any document scrollbars.
		if ( $el.css('position') === 'fixed' ) {
			css.top -= Math.max( document.documentElement.scrollTop, document.body.scrollTop );
			css.left -= Math.max( document.documentElement.scrollLeft, document.body.scrollLeft );
		}

		return css;
	};

if ( $.widget ) {

	// Create a css-based scrollbar.
	$.widget( "cms.cssScroller", {
		options: {
		},

		_create: function() {
			// Assign the scrollbar.
			this.element
				.css({
					overflow: 'hidden',
					overflowX: 'hidden',
					overflowY: 'hidden'
				})
				.bind('mousewheel', $.proxy(this._handleWheel, this));

			this.elements = {};

			// Get the scrollbar.
			this.elements.scrollbar = $('<div class="ui-scroller"><div class="ui-scroller-handle"></div></div>')
				.insertAfter(this.element)
				.css({
					position: 'absolute',
					top: 0,
					right: 0,
					height: '100%'
				})
				.bind('mousewheel', $.proxy(this._handleWheel, this))
				.bind('click', $.proxy(this._handleClick, this));

			// And the handle.
			this.elements.handle = this.elements.scrollbar
				.children('div.ui-scroller-handle')
				.draggable({
					axis: 'y',
					containment: 'parent',
					start: $.proxy(this._drag, this),
					drag: this._dragging,
					stop: this._drop
				});

			// Set the initial size.
			this.resize();

			// Resize the scrollbar when the window is resized.
			$(window).bind('resize', $.proxy(this.resize, this));
		},

		// Calculate the scroll position.
		_getScroll: function() {
			// See if we need to scroll the original element.
			var p = this.element[0];
			var offset = p.offsetHeight;
			var top = p.scrollTop;

			// Note that MSIE6/7 fucks up the scroll height with "hidden".
			var scroll;
			if ( $.browser.msie && $.browser.version <= 7 && p.childNodes.length ) {
				var first = p.firstChild.offsetTop - $.toInt($.curCSS(p.firstChild, 'marginTop'));
				var last = p.lastChild.offsetTop + p.lastChild.offsetHeight;
				scroll = last - first;
			} else {
				scroll = p.scrollHeight;
			}

			// Get the css.
			var ratio = ( scroll && offset ) ? ( 1.000 * offset / scroll ) : 1.000;
			
			// Return the results.
			return {
				offset: offset,
				scroll: scroll,
				ratio: ratio,
				top: top,
				newheight: parseInt( ratio * offset ),
				newtop: parseInt( ratio * top )
			};
		},

		// Resize the scrollbar.
		resize: function() {
			var scroll = this._getScroll();

			var css;
			if ( scroll.ratio >= 1 ) {
				css = { display: 'none' };
			} else {
				// Calculate the height and position of the scroller widget.
				var css = {
					display: 'block',
					height: scroll.newheight,
					top: scroll.newtop||0
				};
			}

			// Set the css.
			this.elements.handle.css(css);
		},

		// Clicked on the scrollbar.
		_handleClick: function(e) {
			// If we clicked on the drag handle itself, do nothing.
			if ( e.target === this.elements.handle[0] ) {
				return;
			} else if ( e.target === this.elements.scrollbar[0] && this.elements.handle.is(':visible') ) {
				// Adjust the scroll position.
				var y = e.clientY + document.documentElement.scrollTop;
				var dim = this.elements.handle.dimensions();
				if ( y > dim.top + dim.height ) {
					this.element[0].scrollTop += this.element[0].offsetHeight;
				} else if ( y < dim.top ) {
					this.element[0].scrollTop -= this.element[0].offsetHeight;
				} else {
					return;
				}

				// And the handle position.
				var scroll = this._getScroll();
				this.elements.handle.css({top: scroll.newtop});
				return false;
			}
		},

		// If we move the scrollwheel, scroll the element.
		_handleWheel: function(e) {
			var p = this.element[0];
			if ( p.scrollHeight > p.offsetHeight ) {
				// Adjust the scroll position.
				var offset = 0-(e.delta*70);
				p.scrollTop += offset;

				// And the handle position.
				var scroll = this._getScroll();
				this.elements.handle.css({top: scroll.newtop});
				return false;
			}
		},
		
		// Note the positions at the start of the drag.
		_drag: function(e, ui) {
			// Get the scroll data for the drag.
			ui.helper.position = this._getScroll();
			ui.helper.position.parent = this.element[0];
		},
		
		// Update the position.
		_dragging: function(e, ui) {
			var scroll = (ui.position.top - ui.originalPosition.top) / ui.helper.position.ratio;
			ui.helper.position.parent.scrollTop = ui.helper.position.top + parseInt(scroll);
		},

		// Clean up the temp variables after the drag is finished.
		_drop: function(e, ui) {
			ui.helper.position.parent = null;
			ui.helper.position = null;
		}
	});


	// Create a loading widget.
	$.widget( "cms.loading", {
		options: {
			timeout: 20000,			// 20 seconds
			message: '&nbsp;',		// Override with null to not display any message.
			color: '#000000',
			opacity: 0.10,			// 25%
			relativeto: null,		// To position the message relative to a different element than the overlay.
			zIndex:null
		},

		overlay: null,
		// reuse old instances due to IE memory leak with alpha transparency (see #5185)
		oldOverlay: null,

		// Create the loading screen.
		_create: function() {
			if (!this.overlay) {
				// If the element isn't visible, don't show the loading screen.
				if ( !this.element.is(':visible') ) {
					return;
				}

				// Get the overlay.
				this.overlay = (this.oldOverlay || $('<div></div>'))
					.addClass('ui-widget-overlay')
					.prependTo(this.attach())
					.css(this._overlayCss());

				// Set the z-index if so specified.
				if (this.options.zIndex) {
					this.overlay.css({zIndex:this.options.zIndex});
				} else {
					// Ensure the overlay will not be behind the element it is covering.
					var z = $.toInt(this.element.css('z-index'))+1;

					var node = this._node();
					switch ( node ) {
						case 'window':
						case 'document':
						case 'body':
							// Get the native z-index of the overlay.
							z = Math.max(z, $.toInt(this.overlay.css('z-index')) );
							// Compare the overlay z-index to any existing dialog boxes or overlays.
							z = Math.max(z, $.ui.dialog.maxZ+1, $.ui.dialog.overlay.maxZ+1);
							break;
						default:
							// Otherwise, we'll show the overlay just below the admin toolbar.
							z = Math.max(z,99);
							break;
					}
					this.overlay.css({zIndex: z});
				}

				// Null out any old overlay.
				this.oldOverlay = null;

				// Add an optional message.
				this._setMessage();

				// Bigiframe it as needed.
				if ($.fn.bgiframe) {
					this.overlay.bgiframe();
				}

				// Set a timeout to kill the loading element.
				if (this.options.timeout)
					this._timeout = setTimeout($.proxy(this._kill, this), this.options.timeout);
			}
		},

		// What is the node name of the current element.
		_node: function() {
			var elem = this.element.length && this.element[0];
			if (!elem) return null;

			// Get the node name.
			if ("scrollTo" in elem && elem.document)
				return 'window';
			else if (elem.nodeType === 9)
				return 'document';
			else
				return elem.nodeName && elem.nodeName.toLowerCase();
		},

		// Where to attach the loading element to?
		_attach: null,
		attach: function() {
			var $el = this._attach;
			if (!$el) {
				var node = this._node();
				switch (node)
				{
					case 'window':
						$el = $(this.element[0].document.body);
						break;
					case 'document':
						$el = $(this.element[0].body);
						break;
					case 'table':
					case 'tbody':
					case 'tr':
						// Find the first child td.
						$el = this.element.find('td:visible:first');
						if (!$el.length) {
							// If no child tds, find the parent of the nearest table.
							$el = this.element.closest('table').parent();
						}
						break;
					case 'body':
					case 'td':
						// A body or td can hold the loading element.
						$el = this.element;
						break;
					case 'div':
						if ( /auto|scroll|hidden/.test(this.element.css('overflow')) ) {
							// If the element we're "loading" over is a scrolling div, attach to the parent.
							$el = this.element.parent();
						} else {
							$el = this.element;
						}
						break;
					default:
						// Otherwise, find the nearest parent that matches that criteria.
						$el = this.element.parents().filter('div,td,body').filter(':visible').eq(0);
						break;
				}
				this._attach = $el;
			}
			return $el;
		},

		// Where to position the message?
		relative: function() {
			if ( this.options.relativeto ) {
				return this.options.relativeto;
			} else {
				var node = this._node();
				switch (node)
				{
					case 'window':
					case 'document':
					case 'body':
						return $(window);
					default:
						return this.element;
				}
			}
		},

		// The size and position of the overlay element.
		_overlayCss: function(relative) {
			// Get the element the css will be relative to.
			var $el = (relative||this.element);

			// Get the standard css.
			var css = $el.dimensions();
			css.backgroundColor = this.options.color||'#000000';
			css.opacity = this.options.opacity||.25;

			// Adjust for any offset.
			if ( !this.attach().is('body') ) {
				var op = this.attach().css('position') === 'static' ? this.attach().offsetParent() : this.attach();
				var o = op.offset();
				css.left -= o.left;
				css.top -= o.top;
			}

			return css;
		},

		// Set or update the message element.
		_setMessage : function() {
			if (this.options.message) {
				// Get the overlay CSS, relative to the specified element.
				var overlay = this._overlayCss(this.relative());

				// Build the message element as needed.
				var shadow = false;
				if ( !this.message ) {
					shadow = true;
					this.message = $('<div><div></div></div>')
						.addClass('ui-widget-loading')
						.css({position:'absolute'})
						.children('div')
							.addClass('ui-widget-loading2')
							.html(this.options.message)
						.end()
						.insertBefore(this.overlay);
				}

				// Get the position of the message.
				var w = this.message.width();
				var h = this.message.height();
				var css = {
					left: overlay.left + parseInt((overlay.width-w)/2),
					top: overlay.top + parseInt((overlay.height-h)/2),
					zIndex: $.toInt(this.overlay.css('z-index'))+1
				};

				// Set the CSS of the element.
				this.message.css(css);

				// Set the shadow as needed.
				if ( shadow ) {
					this.message.shadow();
				}
			}
			else
				this.message = null;
		},

		// If we timed out.
		_kill: function() {

			if ( this.element.is(':visible') ) {
				var options = this.attach[0] === document.body ? { modal: false } : null;
				$.alert('Time Out','The process could not be completed', options);
			}
			this.destroy();
		},

		// Update the size and position of the loading panel.
		update: function() {
			this.overlay.css(this._overlayCss());
			this._setMessage();
		},

		// User-friendly alias for destroy.
		done: function() {
			this.destroy();
		},

		// Completely remove the loading element.
		destroy: function() {
			this._attach = null;
			if (this._timeout) {
				clearTimeout(this._timeout);
				this._timeout = null;
			}
			if (this.message) {
				this.message.remove();
				this.message = null;
			}
			if (this.overlay) {
				this.oldOverlay = this.overlay;
				this.overlay.remove();
				this.overlay = null;
			}
			$.Widget.prototype.destroy.apply( this, arguments );
		}
	});

	$.fn.doneLoading = function(){
		return this.loading('done');
	};

	// A cross-browser drop shadow (for MSIE it relies on CSS background image png).
	$.widget( "cms.shadow", {
		options: {
			hide: false,
			width: 4,
			opacity: 0.25
		},

		// Create the shadow
		_create: function() {
			if ($.browser.msie) {
				this._msieShadow();
			} else {
				var w = this.options.width;
				var o = this.options.hide ? 0 : this.options.opacity;
				var prop = $.cms.shadow.property();
				var val = $.cms.shadow.css(w-1, w-1, w, o);
				this.element.css(prop, val);

				// Ensure we have a custom animation method for the box shadow.
				if ( $.fx.step['boxShadow'] === undefined ) {
					$.fx.step['boxShadow'] = function(fx) {
						// Initialize the start and end values for the box-shadow.
						var prop = $.cms.shadow.property();
						if (!fx.shadowInit) {
							var start = $.cms.shadow.parse( $.curCSS(fx.elem, prop) );
							if (start) {
								fx.start = start;
								fx.parsed = true;
								fx.end = $.cms.shadow.parse( fx.end + fx.unit );
							}
							fx.shadowInit = true;
						}

						// Assign the calculated property.
						if (fx.parsed) {
							var h = parseInt( ( fx.pos * ( fx.end.h - fx.start.h ) ) + fx.start.h, 10 );
							var v = parseInt( ( fx.pos * ( fx.end.v - fx.start.v ) ) + fx.start.v, 10 );
							var b = parseInt( ( fx.pos * ( fx.end.b - fx.start.b ) ) + fx.start.b, 10 );
							var o = Math.max( Math.min( ( fx.pos * ( fx.end.o - fx.start.o ) ) + fx.start.o, 1 ), 0 );
							fx.elem.style[prop] = $.cms.shadow.css(h, v, b, o);
						}
					}
				}
			}
		},

		// MSIE shadow is handled by child divs with background images.
		_msieShadow: function() {
			// The shadow only works on a non-static div element.
			if ( $.browser.version > 6 &&
				 this.element.is('div') && 
				 this.element.css('position') != 'static' ) {

				// Create the shadow.
				this.shadow = [
					$('<div class="ui-shadow-right"></div>').css({position:'absolute'}).appendTo(this.element),
					$('<div class="ui-shadow-corner"></div>').css({position:'absolute'}).appendTo(this.element),
					$('<div class="ui-shadow-bottom"></div>').css({position:'absolute'}).appendTo(this.element)
				];

				if ( this.options.hide ) {
					this.shadow[0].css({display:'none'});
					this.shadow[1].css({display:'none'});
					this.shadow[2].css({display:'none'});
				}

				// Get the element dimensions.
				var dim = this.element.dimensions();
				var border = {
					top: $.toInt(this.element.css('borderTopWidth')),
					left: $.toInt(this.element.css('borderLeftWidth'))
				};

				// Get the width and height of the shadow.
				var w = $.toInt($.curCSS(this.shadow[0][0], 'width'));
				var h = $.toInt($.curCSS(this.shadow[2][0], 'height'));

				// Set the right shadow.
				this.shadow[0].css({
					top: border.top,
					height: '100%',
					right: 0-(w+border.left)
				});

				// Set the corner shadow.
				this.shadow[1].css({
					right: 0-(w+border.left),
					bottom: 0-(h+border.top)
				});

				// Set the bottom shadow.
				this.shadow[2].css({
					width: '100%',
					left: border.left,
					bottom: 0-(h+border.top)
				});
			}
		},

		// Get the current drop shadow CSS for a particular opacity.
		getCSS: function(opacity) {
			var w = this.options.width;
			var o = typeof opacity === 'number' ? opacity: this.options.opacity;
			return $.cms.shadow.css(w-1, w-1, w, o);
		},

		show: function() {
			if ( $.browser.msie ) {
				this.shadow[0].css({display:'block'});
				this.shadow[1].css({display:'block'});
				this.shadow[2].css({display:'block'});
			} else {
				var css = {};
				css[ $.cms.shadow.property() ] = this.getCSS();
				this.element.css(css);
			}
		},

		hide: function() {
			if ( $.browser.msie ) {
				this.shadow[0].css({display:'none'});
				this.shadow[1].css({display:'none'});
				this.shadow[2].css({display:'none'});
			} else {
				var css = {};
				css[ $.cms.shadow.property() ] = this.getCSS(0);
				this.element.css(css);
			}
		},

		// Completely remove the loading element.
		destroy: function() {
			$.Widget.prototype.destroy.apply( this, arguments );
		}
	});

	// Static drop shadow methods.
	$.extend($.cms.shadow, {
		// The browser-specific property.
		property: function(){
			if ($.browser.webkit) {
				return '-webkit-box-shadow';
			} else if ($.browser.mozilla) {
				return '-moz-box-shadow';
			} else {
				return 'box-shadow';
			}
		},

		// The calculated CSS for the property.
		css: function(horizontal, vertical, blur, opacity) {
			// Calculate the value.
			var val = [];
			val.push(horizontal);
			val.push('px ');
			val.push(vertical);
			val.push('px ');
			val.push(blur);
			val.push('px rgba(0, 0, 0, ');
			val.push(opacity);
			val.push(')');

			// Return the css.
			return val.join('');
		},

		// Parse out the shadow.
		r_shadow: /(?:\b(\d+)px|rgba\(\d+,\s*\d+,\s*\d+,\s*([\d\.]+)\s*\))/gi,

		// Parse the box shadow property into its component parts.
		parse: function(val) {
			if (!val) {
				return null;
			}

			// Use the regex to extract the elements.
			var opacity = 0.00;
			var shadow = [];
			var m;
			while ( m = $.cms.shadow.r_shadow.exec(val) ){
				if ( m[1] ) {
					shadow.push( parseInt(m[1], 10) );
				} else if ( m[2] ) {
					opacity = parseFloat(m[2], 10);
				}
			}

			// Return the value.
			if ( shadow.length ) {
				return {
					h: shadow[0],
					v: shadow.length > 0 ? shadow[1] : shadow[0],
					b: shadow.length > 1 ? shadow[2] : shadow[0],
					o: opacity
				};
			} else {
				return null;
			}
		}
	});

	// A fancy tooltip.
	$.widget( "cms.tooltip", {
		options: {
			out: false,
			place: {
				my: 'left top',
				at: 'left bottom',
				collision: 'none none',
				offset: null
			}
		},

		// Create the shadow
		_create: function() {
			// Create the tooltip.
			var cls = this.options.custom || 'ui-tooltip';
			this.tooltip = $('<div class="'+cls+'"><div><span></span></div></div>')
				.css({
					position: 'absolute',
					display: 'none'
				});

			// We don't want the tooltip to be hidden inside an overflow element.
			var p;
			if ( this.options.out && (p=this.element.overflowParent()) ) {
				this.tooltip.insertBefore(p);
			} else {
				this.tooltip.insertBefore(this.element);
			}

			if ( $.browser.msie ) {
				this.tooltip.addClass('ie');
			}

			// Set the tooltip title.
			this.update();

			// Set the tooltip to fade in if this isn't IE.
			if ( !$.browser.msie ) {
				this.tooltip.css({opacity:0});
			}

			// Bind the events.
			var fn = $.proxy(this._setTooltip, this);
			this.element.bind('mouseover mouseout focus blur', fn );
			if ( this.options.label ) {
				this.options.label.bind('mouseover mouseout', fn );
			}
		},

		// Update the title.
		update: function(title) {
			if ( title === undefined ) {
				title = this.element.attr('title');
			} else {
				this.element.attr('title',title);
			}
			// Update the title.
			this.title = title;
			if ( this.title ) {
				this.tooltip.find('span').html( this.title );
				this.element.removeAttr('title');
				if ( this.tooltip.is(':visible') ) {
					this.show();
				}
			}
		},

		// Set the tooltip state.
		_setTooltip: function(e) {
			if ( !$.browser.msie ) {
				// Stop or hide any animation in progress.
				this.tooltip.stop();
			}

			// Clear any timeout.
			if ( this._timeout ) {
				clearTimeout(this._timeout);
				this._timeout = null;
			}

			// Show or hide the tooltip.
			switch (e.type) {
				case 'mouseover':
					if ( !$.cms.tooltip.focused ) {
						this.show();
					}
					return;
				case 'focus':
					if ( this.element.is('input:text') ) {
						$.cms.tooltip.focused = this;
						this.show();
					}
					return;
				case 'mouseout':
					if ( $.cms.tooltip.focused !== this ) {
						var fn = function(tooltip){
							return function(){
								tooltip.hide();
								tooltip = null;
							};
						}(this);
						this._timeout = setTimeout(fn, 100);
					}
					return;
				case 'blur':
					if ( this.element.is('input:text') ) {
						$.cms.tooltip.focused = null;
						this.hide();
					}
					return;
			}
		},

		// Show the tooltip.
		show: function() {
			if ( !this.title ) {
				return;
			}
			// Position the tooltip.
			this.tooltip.place({
				my: this.options.place.my,
				at: this.options.place.at,
				of: this.options.place.of || this.element,
				offset: this.options.place.offset,
				collision: this.options.place.collision
			});

			if ( !$.browser.msie ) {
				this.tooltip.animate( {opacity:0.85}, 75 );
			}
		},

		// Hide the tooltip.
		hide: function() {
			if ( !$.browser.msie ) {
				this.tooltip.animate({opacity:0}, 150, function(e){ $(this).hide(); } );
			} else {
				this.tooltip.hide();
			}
		},

		// Completely remove the loading element.
		destroy: function() {
			$.Widget.prototype.destroy.apply( this, arguments );
		}
	});

	// A fancy tooltip.
	$.widget( "cms.inputStyle", {
		options: {
			tooltip: true
		},

		_create: function() {
			if ( this.element.is('input:text,input:password') ) {
				this._createTextBox();
			} else if ( this.element.is('input:checkbox') ) {
				this._createCheckBox();
			} else if ( this.element.is('input:radio') ) {
				this._createRadio();
			} else if ( this.element.is('input:hidden.thumbnail') ) {
				this._createThumbnail();
			} else if ( this.element.is('textarea') ) {
				this._createTextArea();
				this._createTextBox();
			}
		},

		// Create a styleized text box.
		_createTextBox: function() {
			// Add any special selector buttons.
			var btns = [];

			// Check for special url buttons.
			if ( this.element.is('.document') ) {
				this._configureDocument(btns);
			} else if ( this.element.is('.flash') ) {
				this._configureFlash(btns);
			} else if ( this.element.is('.video') ) {
				this._configureVideo(btns);
			}

			// And other special buttons.
			if ( this.element.is('.color') ) {
				this._configureColor(btns);
			} else if ( this.element.is('.link') ) {
				this._configureLink(btns);
			} else if ( this.element.is('.page') ) {
				this._configurePage(btns);
			} else if ( this.element.is('.adminpage') ) {
				this._configureAdminPage(btns);
			} else if ( this.element.is('.image') ) {
				this._configureImage(btns);
			} else if ( this.element.is('.date,.time') ) {
				this._configureDate(btns);
			} else if ( this.element.is('.ds') ) {
				this._configureDataSource(btns);
			} else if ( this.element.is('.pages') ) {
				this._configureMultiplePages(btns);
			}

			if ( btns.length ) {
				this._buttons = $( btns );
				this.element.attr('spellcheck',false).css({verticalAlign:'middle'});
				var w = $.toInt( $.curCSS( this.element[0], 'width' ) ) - ( 23 * btns.length );
				if ( w > 5 ) {
					this.element.css({width:w});
				}
			}

			// Set the tooltip.
			if ( this.options.tooltip && this.element.attr('title') ) {
				this.element.tooltip({
					place: {
						offset: {left: -5, top: 1}
					}
				});
			}
		},

		// Configure the color picker.
		_configureColor: function(btns) {
			var fn = $.proxy(this._chooseColor, this);

			// Add the color icon.
			var btn = $('<img src="/Shared/images/spacer.gif" class="itr itr_color" style="vertical-align:middle;cursor:pointer;margin:1px 1px 0px 0px;" >')
				.insertAfter( this.element )
				.bind('click', fn )
				.attr('title', 'Select Color');
			btns.push( btn[0] );

			// Bind to the input element directly.
			this.element.bind('focus.color keydown.color blur.color', fn );
		},

		// Configure the page picker(s).
		_configurePage: function(btns) {
			// Add the system page chooser, if appropriate.
			if ( $.cms.page.PPM(1,2) ) {
				var btn = $('<img src="/Shared/images/spacer.gif" class="itr itr_system" style="vertical-align:middle;cursor:pointer;margin:1px 1px 0px 0px;" />')
					.insertAfter( this.element )
					.bind('click', $.proxy(this._selectSystemPage, this) )
					.attr('title', 'Select System  Page');
				btns.push( btn[0] );
			}

			if ( !this.element.is('.system') ) {
				// Add the admin page chooser, if appropriate.
				if ( $.cms.page.AdminTemplateID() > 0 ) {
					var btn = $('<img src="/Shared/images/spacer.gif" class="itr itr_system itr_locked" style="vertical-align:middle;cursor:pointer;margin:1px 1px 0px 0px;" />')
						.insertAfter( this.element )
						.bind('click', $.proxy(this._selectAdminPage, this) )
						.attr('title', 'Select Admin Page');
					btns.push( btn[0] );
				}

				// Add the standard page chooser.
				var btn = $('<img src="/Shared/images/spacer.gif" class="itr itr_page" style="vertical-align:middle;cursor:pointer;margin:1px 1px 0px 0px;" />')
					.insertAfter( this.element )
					.bind('click', $.proxy(this._selectPage, this) )
					.attr('title', 'Select Page');
				btns.push( btn[0] );
			}
		},

		// Configure the page picker.
		_configureAdminPage: function(btns) {
			var btn = $('<img src="/Shared/images/spacer.gif" class="itr itr_system itr_locked" style="vertical-align:middle;cursor:pointer;margin:1px 1px 0px 0px;" />')
				.insertAfter( this.element )
				.bind('click', $.proxy(this._selectAdminPage, this) )
				.attr('title', 'Select Admin Page');
			btns.push( btn[0] );
		},

		// Configure the page browser (no admin or system page options).
		_configureLink: function(btns) {
			// Add the standard page chooser.
			var all = this.element.is('.all'),
				btn = $('<img src="/Shared/images/spacer.gif" class="itr itr_link" style="vertical-align:middle;cursor:pointer;margin:1px 1px 0px 0px;" />')
				.insertAfter( this.element )
				.bind('click', $.proxy(this._selectLink, this) )
				.attr('title', all ? 'Select Link' : 'Select Page' );
			btns.push( btn[0] );

			// Add the standard page chooser.
			if ( all ) {
				btn = $('<img src="/Shared/images/spacer.gif" class="itr itr_page" style="vertical-align:middle;cursor:pointer;margin:1px 1px 0px 0px;" />')
					.insertAfter( this.element )
					.bind('click', $.proxy(this._selectPage, this) )
					.attr('title', 'Select Page');
				btns.push( btn[0] );
			}
		},

		// Configure the image picker.
		_configureImage: function(btns) {
			// Add the standard page chooser.
			var btn = $('<img src="/Shared/images/spacer.gif" class="itr itr_picture" style="vertical-align:middle;cursor:pointer;margin:1px 1px 0px 0px;" />')
				.insertAfter( this.element )
				.bind('click', $.proxy(this._selectImage, this) )
				.attr('title', 'Select Image');
			btns.push( btn[0] );
		},

		// Configure the document picker.
		_configureDocument: function(btns) {
			// Add the standard page chooser.
			var btn = $('<img src="/Shared/images/spacer.gif" class="itr itr_file" style="vertical-align:middle;cursor:pointer;margin:1px 1px 0px 0px;" />')
				.insertAfter( this.element )
				.bind('click', $.proxy(this._selectDocument, this) )
				.attr('title', 'Select Document');
			btns.push( btn[0] );
		},

		// Configure the flash picker.
		_configureFlash: function(btns) {
			// Add the standard page chooser.
			var btn = $('<img src="/Shared/images/spacer.gif" class="itr itr_flash" style="vertical-align:middle;cursor:pointer;margin:1px 1px 0px 0px;" />')
				.insertAfter( this.element )
				.bind('click', $.proxy(this._selectFlash, this) )
				.attr('title', 'Select Flash');
			btns.push( btn[0] );
		},

		// Configure the video picker.jeffkretz	Dkti49ra34#

		_configureVideo: function(btns) {
			// Add the standard page chooser.
			var btn = $('<img src="/Shared/images/spacer.gif" class="itr itr_video" style="vertical-align:middle;cursor:pointer;margin:1px 1px 0px 0px;" />')
				.insertAfter( this.element )
				.bind('click', $.proxy(this._selectVideo, this) )
				.attr('title', 'Select Video');
			btns.push( btn[0] );
		},

		// Configure the date picker.
		_configureDate: function(btns) {
			var fn = $.proxy(this._chooseDate, this),
				cls = this.element.is('.date') ? 'itr_calendar' : 'itr_clock';

			// Add the color icon.
			var btn = $('<img src="/Shared/images/spacer.gif" class="itr '+cls+'" style="vertical-align:middle;cursor:pointer;margin:1px 1px 0px 0px;" >')
				.insertAfter( this.element )
				.bind('click', fn )
				.attr('title', 'Select Date');
			btns.push( btn[0] );

			// Bind to the input element directly.
			this.element.bind('focus.date keydown.date blur.date', fn );

			// Get the datepicker info.
			this._date = this.element.is('.date');
			this._time = this.element.is('.time');
			this._pattern = this.element.attr('_pattern');
			if ( !this._pattern ) {
				if ( this._date && this._time ) {
					this._pattern = "M/d/yyyy h:mmtt";
				} else if ( this._time ) {
					this._pattern = "h:mmtt";
				} else {
					this._pattern = "M/d/yyyy";
				}
			}
		},

		// Configure the datasource picker.
		_configureDataSource: function(btns) {
			if ( $.cms.datasources ) {
				var btn = $('<img src="/Shared/images/spacer.gif" class="itr itr_document" style="vertical-align:middle;cursor:pointer;margin:1px 1px 0px 0px;" />')
					.insertAfter( this.element )
					.bind('click', $.proxy(this._selectDataSource, this) )
					.attr('title', 'Select DataSource');
				btns.push( btn[0] );
			}
		},

		// Configure the multiple page picker.
		_configureMultiplePages: function(btns) {
			var btn = $('<img src="/Shared/images/spacer.gif" class="itr itr_page" style="vertical-align:middle;cursor:pointer;margin:1px 1px 0px 0px;" />')
				.insertAfter( this.element )
				.bind('click', $.proxy(this._selectMultiplePages, this) )
				.attr('title', 'Select Pages');
			btns.push( btn[0] );
		},

		// Activate the color picker.
		_chooseColor: function(e) {
			switch (e.type) {
				case 'keydown':
					if ( $.cms.colorpicker.active ) {
						$.cms.colorpicker.prototype.deactivate();
					}
					break;
				case 'focus':
				case 'click':
					$.cms.colorpicker.prototype.activate({
						hash: !this.element.is('.nohash'),
						input: this.element
					});
					break;
				case 'blur':
					// How did this happen?
					var x = 1;
					break;
			}
		},

		// Activate the date picker.
		_chooseDate: function(e) {
			switch (e.type) {
				case 'keydown':
					if ( $.cms.datepicker.active ) {
						$.cms.datepicker.prototype.deactivate();
						if ( e.which === 27 ) {
							e.preventDefault();
							e.stopPropagation();
							e.stopImmediatePropagation();
							return false;
						}
					}
					break;
				case 'focus':
				case 'click':
					$.cms.datepicker.prototype.activate({
						input: this.element,
						showdate: this._date,
						showtime: this._time,
						pattern: this._pattern
					});
					break;
				case 'blur':
					// How did this happen?
					var x = 1;
					break;
			}
		},

		// Select a standard page.
		_selectPage: function(e) {
			var href = this.element.val() || window.location.href.split('?').shift();
			$.cms.pagebrowser.prototype.activate({
				href: href && $.cms.page ? $.cms.page.CleanUrl(href) : href,
				onsave: this._savePage(this.element),
				sub: this.element.is('.sub')
			});
		},

		// Select an admin page.
		_selectAdminPage: function(e) {
			var href = this.element.val() || window.location.href.split('?').shift();
			$.cms.adminpagebrowser.prototype.activate({
				href: href && $.cms.page ? $.cms.page.CleanUrl(href) : href,
				onsave: this._savePage(this.element),
				sub: this.element.is('.sub')
			});
		},

		// Select a system page.
		_selectSystemPage: function(e) {
			var system = $.toInt(this.element.attr('_system'));
			$.cms.systempagebrowser.prototype.activate({
				href: this.element.val(),
				onsave: this._savePage(this.element),
				sub: this.element.is('.sub'),
				tree: {
					fields: system,
					expandall: system > 0
				}
			});
		},

		// Select multiple standard pages.
		_selectMultiplePages: function(e) {
			var pages = $.toIntArray( this.element.val() );
			$.cms.pagebrowser.prototype.activate({
				tree: {
					pages: pages.length > 1 ? pages : null,
					multiple: true,
					clickonoff: true
				},
				onsave: this._savePages(this.element),
				sub: this.element.is('.sub')
			});
		},

		// Select a datasource.
		_selectDataSource: function(e) {
			$.cms.datasources.prototype.activate({
				href: this.element.val(),
				onsave: $.proxy(this._saveDataSource, this)
			});
		},

		// Save the selected datasource.
		_saveDataSource: function(e, ds, params, wz) {
			this.element.val(ds.Path);
		},

		// Return a standard closure to save a page value.
		_savePage: function(input) {
			return function(e, page){
				if ( page ) {
					if ( page.href ) {
						input.val(page.href).trigger('change',[page]);
					} else {
						if ( input.is('.pageid') ) {
							var hidden = input.prevUntil('input:hidden').eq(0).prev('input:hidden');
							if ( hidden.length ) {
								hidden.val( page.AdminPageID || page.PageID || page.SystemPageID );
							} else {
								input.data('pageid',  page.AdminPageID || page.PageID || page.SystemPageID );
							}
						}
						input.val(page.Path).trigger('change',[page]);
					}
				}
				input = null;
			};
		},

		// Return a closure to save multiple page values.
		_savePages: function(input) {
			return function(e, pages){
				if ( pages ) {
					var sb = [];
					for ( var i=0; i<pages.length; i++ ) {
						sb.push( pages[i].PageID );
					}
					input.val( sb.join(',') );
				}
			};
		},

		// Return a standard closure to save a file value.
		_saveFile: function(input) {
			return function(files){
				if ( files ) {
					var file = $.isArray(files) ? files[0] : files;
					if ( file && file.Path ) {
						input.val(file.Path).trigger('update',[file]).trigger('change',[file]);
					}
				}
				input = null;
			};
		},

		// Activate the link browser.
		_selectLink: function(e) {
			var link = this.element.data('link'),
				href = this.element.val(),
				wz = this.element.is('.all') ? $.cms.linkmanager : $.cms.pagebrowser;

			wz.prototype.activate({
				link: link,
				href: href && $.cms.page ? $.cms.page.CleanUrl(href) : href,
				onsave: this._savePage(this.element),
				sub: this.element.is('.sub'),
				notext: this.element.is('.notext'),
				showcustom: !this.element.is('.nocustom')
			});
		},

		// Activate the image browser.
		_selectImage: function(e) {
			var options = {
				path: this.element.val(),
				onsave: this._saveFile(this.element),
				sub: this.element.is('.sub')
			};

			if ( this.element.is('.shared') ) {
				options.tfiles = 'shared';
			} else if ( this.element.is('.tfiles') ) {
				options.tfiles = 'true';
			} else if ( $.cms.filebrowser.prototype.options.tfiles === 'mobile' ) {
				options.tfiles = 'mobile';
			} else {
				options.tfiles = null;
			}

			if ( this.element.is('.shared.vpics') ) {
				// Open the shared image browser.
				options.tree = { fields: '/Shared/videos/vpics' };
				$.cms.imagebrowser.prototype.activate(options);
			} else if ( this.element.is('.shared') ) {
				// Open the shared image browser.
				options.tree = { fields: '/Shared/images2' };
				$.cms.imagebrowser.prototype.activate(options);
			} else if ( this.element.is('.vpics') ) {
				// Open the vpics image browser.
				options.tree = { fields: '/media/vpics' };
				$.cms.videobrowser.prototype.activate(options);
			} else {
				$.cms.imagebrowser.prototype.activate(options);
			}
		},

		// Activate the document browser.
		_selectDocument: function(e) {
			$.cms.documentbrowser.prototype.activate({
				path: this.element.val(),
				onsave: this._saveFile(this.element),
				sub: this.element.is('.sub'),
				tfiles: this.element.is('.tfiles') ? 'true' : null
			});
		},

		// Activate the flash browser.
		_selectFlash: function(e) {
			$.cms.flashbrowser.prototype.activate({
				path: this.element.val(),
				onsave: this._saveFile(this.element),
				sub: this.element.is('.sub'),
				tfiles: this.element.is('.tfiles') ? 'true' : null
			});
		},

		// Activate the video browser.
		_selectVideo: function(e) {
			var options = {
				path: this.element.val(),
				onsave: this._saveFile(this.element),
				sub: this.element.is('.sub'),
				tfiles: this.element.is('.shared') ? 'shared' : ( this.element.is('.tfiles') ? 'true' : null )
			};

			if ( this.element.is('.spotlight') ) {
				options.tree = { fields: '/media/spotlight' };
			} else if ( this.element.is('.shared') ) {
				options.tree = { fields: '/Shared/videos' };
			} else if ( !options.path && this.element.is('.hd') ) {
				options.path = '/media/HD/';
			}
			$.cms.videobrowser.prototype.activate(options);
		},

		// Create the stylized checkbox.
		_createCheckBox: function() {
			// Set up the new checkbox.
			var hover = $.proxy(this._checkHover, this);
			this.element.hide() .bind('update', $.proxy(this.update, this) );
			this.checkbox = $('<img class="ui-check-style" src="/Shared/images/spacer.gif">')
				.insertAfter(this.element)
				.bind('click', $.proxy(this._check, this) )
				.bind('mouseover mouseout', hover );

			// Note if this is a red checkbox.
			if ( this.element.is('.red') || this.element.parent('span.red').length ) {
				this.checkbox.addClass('red');
			}

			// Note the state of the checkbox.
			if ( this.element.is(':checked') ) {
				this.checkbox.addClass('checked');
			}
			if ( this.disabled() ) {
				this.checkbox.addClass('disabled');
			}

			var title = this.element.attr('title');
			if ( !title ) {
				 title = this.element.parent('span').attr('title');
				 if ( title ) {
					this.element.parent('span').removeAttr('title');
				 }
			}
			if ( title ) {
				this.origTitle = title;
				this.checkbox.attr('title', title);
				this.element.removeAttr('title');
			}

			// See if we have a bound label to this checkbox.
			var id = this.element.attr('id');
			if ( id ) {
				this.checkbox.attr('for',id);
				this.label = this.element.parent().parent().find("label[for='"+id+"']");
				this.label
					.addClass('ui-check-style')
					.unselectable()
					.bind('click', $.proxy(this._check, this) )
					.bind('mouseover mouseout', hover );
			}

			// Set the tooltip.
			if ( this.options.tooltip ) {
				var place;
				if ( this.element.is('.toolbottom') ) {
					place = {
						my: 'left top',
						at: 'left bottom',
						offset: {left: -1, top: -1}
					};
				} else if ( this.element.is('.toolleft') ) {
					place = {
						my: 'right center',
						at: 'left center',
						offset: {left: -1, top: -1}
					};
				} else if ( this.element.is('.toolright') ) {
					place = {
						my: 'left center',
						at: 'right center',
						offset: {left: -1, top: -1}
					};
				} else if ( this.label && this.label.length ) {
					place = {
						my: 'left top',
						at: 'right bottom',
						of: this.label,
						offset: {left: -1, top: -20}
					};
				} else {
					place = {
						my: 'right top',
						at: 'right bottom',
						offset: {left: 5, top: -2}
					};
				}
				this.checkbox.tooltip({
					place: $.extend( place, this.options.tooltip.place ),
					label: this.label,
					out: this.element.is('.out' )
				});
			}
		},

		// Check or uncheck the box.
		_check: function(e) {
			if ( this.disabled() ) {
				return false;
			}

			// Set the state.
			if ( this.checkbox.is('.checked') ) {
				this.checkbox.removeClass('checked');
				this.element.removeAttr('checked');
			} else {
				this.checkbox.addClass('checked');
				this.element.attr('checked', 'checked');
			}

			// Trigger the change event passing whether or not the ctrl key was pressed.
			var evt = {
				type: 'change',
				ctrlKey: e.ctrlKey,
				originalEvent: e
			};
			this.element.trigger(evt);

			// Prevent the default event.
			e.preventDefault();
		},

		// Create the stylized checkbox.
		_createRadio: function() {
			// Set up the new checkbox.
			var hover = $.proxy(this._checkHover, this);
			var radio = $.proxy(this._radio, this);
			this.element.hide()
				.bind('update', $.proxy(this.update, this) )
				.bind('click', radio);
			this.checkbox = $('<img class="ui-radio-style" src="/Shared/images/spacer.gif">')
				.insertAfter(this.element)
				.attr('name', this.element.attr('name') || "" )
				.bind('click', radio )
				.bind('mouseover mouseout', hover );

			if ( this.element.is(':checked') ) {
				this.checkbox.addClass('checked');
			}

			var title = this.element.attr('title');
			if ( title ) {
				this.checkbox.attr('title', title);
				this.element.removeAttr('title');
			}

			// See if we have a bound label to this checkbox.
			var id = this.element.attr('id');
			if ( id ) {
				this.checkbox.attr('for',id);
				this.label = this.element.parent().parent().find("label[for='"+id+"']");
				this.label
					.addClass('ui-check-style')
					.unselectable()
					.bind('click', $.proxy(this._radio, this) )
					.bind('mouseover mouseout', hover );
			}

			// Set the tooltip.
			if ( this.options.tooltip ) {
				this.checkbox.tooltip({
					place: {
						my: 'left top',
						at: 'left bottom',
						offset: {left: 0, top: -3}
					},
					label: this.label
				});
			}
		},

		// Check the radio button.
		_radio: function(e) {
			// If we fired a click event on the radio button iteself, prevent the default action.
			if ( e && e.target === this.checkbox[0] ) {
				e.preventDefault();
			}

			if ( !this.checkbox.is('.checked') ) {
				$("img[name='"+this.checkbox.attr('name')+"']").removeClass('checked');
				this.checkbox.addClass('checked');
				$("input:radio[name='"+this.checkbox.attr('name')+"']").removeAttr('checked');
				this.element.attr('checked', 'checked').trigger('change');
			}
		},

		disable: function() {
			this.element.attr('disabled','disabled');
			this.checkbox.addClass('disabled');
		},

		enable: function() {
			this.element.removeAttr('disabled');
			this.checkbox.removeClass('disabled');
		},

		disabled: function() {
			return this.element.attr('disabled') ? true : false;
		},

		// Update the state of the box.
		update: function(e) {
			// Set the state.
			if ( this.element.is(':checked') ) {
				this.checkbox.addClass('checked');
			} else {
				this.checkbox.removeClass('checked');
			}

			if ( this.element.is(':disabled') ) {
				this.checkbox.addClass('disabled');
			} else {
				this.checkbox.removeClass('disabled');
			}
		},

		// Scroll a specific checkbox into view.
		scroll: function() {
			this.checkbox.scrollIntoView();
		},

		// Hover the checkbox.
		_checkHover: function(e) {
			switch (e.type) {
				case 'mouseover':
					this.checkbox.addClass('over');
					break;
				case 'mouseout':
					this.checkbox.removeClass('over');
					break;
			}
		},

		// Create a hidden-input thumbnail picker.
		_createThumbnail: function(e) {
			var tr = this.element.closest('tr');
			if ( !tr.length ) {
				tr = this.element.parent().closest('div');
			}
			if ( tr.length ) {
				this.select = tr.find('.ui-select').bind('click', $.proxy(this._selectThumbnail, this) );
				this.cancel = tr.find('.ui-cancel').bind('click', $.proxy(this._cancelThumbnail, this) );
				this.thumbnail = tr.find('img.ui-thumb');
			}
		},

		// Activate the image browser.
		_selectThumbnail: function(e) {
			var options = {
				path: this.element.val() || this.element.attr('_default') || null,
				onsave: $.proxy(this._saveThumbnail, this),
				sub: this.element.is('.sub'),
				tfiles: this.element.is('.shared') ? 'shared' : ( this.element.is('.tfiles') ? 'true' : null )
			};

			if ( this.element.is('.shared') ) {
				// Open the shared images folder.
				options.tree = { fields: '/Shared/images2' };
			} else if ( this.element.is('.tfiles.flash') ) {
				// Open the flash browser.
				options.tree = { fields: '/files/flash' };
			} else if ( this.element.is('.vpics') ) {
				// Open the vpics image browser.
				options.tree = { fields: '/media/vpics' };
			}

			$.cms.imagebrowser.prototype.activate(options);
		},

		// Update the thumbnail value.
		_saveThumbnail: function(files) {
			if ( files ) {
				var file = $.isArray(files) ? files[0] : files;
				if ( file && file.Path ) {
					if ( this.thumbnail.length ) {
						var src = this.thumbnail.attr('src').replace( /([&\?]I=)[^&]*/i, '$1'+$.encode(file.Path) );
						this.thumbnail.attr('src',src)
					}
					this.element.val(file.Path).trigger('update',[file]).trigger('change');
				}
			}
		},

		// Remove any thumbnail.
		_cancelThumbnail: function(e) {
			var img = $(e.target),
				def = img.attr('_default') || '/Shared/images/spacer.gif';
			if ( this.thumbnail.length ) {
				var src = this.thumbnail.attr('src').replace( /([&\?]I=)[^&]*/i, '$1'+$.encode(def) );
				this.thumbnail.attr('src',src)
			}
			this.element.val('').trigger('update',[{}]).trigger('change');
		},

		// Set up a textarea.
		_createTextArea: function() {
			this.element.bind('keydown', function(e) {
				if ( e.which === 9 ) {
					if ( !e.shiftKey ) {
						$(this).insertAtCursor('\t');
					}
					e.preventDefault();
					e.stopPropagation();
					e.stopImmediatePropagation();
					return false;
				}
			});
		},

		// Show or hide the element.
		show: function() {
			var el;
			if ( this._buttons ) {
				el = this._buttons;
			} else if ( this.element.is('input:checkbox') ) {
				el = this.checkbox;
			}

			if ( el ) {
				el.show();
			}
		},

		// Show or hide the combobox.
		hide: function() {
			var el;
			if ( this._buttons ) {
				el = this._buttons;
			} else if ( this.element.is('input:checkbox') ) {
				el = this.checkbox;
			}

			if ( el ) {
				el.hide();
			}
		},

		// Update the tooltip.
		tooltip: function(title) {
			var el;
			if ( this.element.is('input:text') ) {
				el = this.element;
			} else if ( this.element.is('input:checkbox') ) {
				el = this.checkbox;
			}

			if ( !el ) {
				el = this.element;
			}

			el.attr('title', title || this.origTitle || "" );
			if ( this.options.tooltip ) {
				el.tooltip('update');
			}
		}
	});

	// Filter the current selection by properties that would required an inputStyle widget.
	$.cms.inputStyle.filter = function(i) {
		var cls = (this.className||"").split(' ');
		for ( var i=0; i<cls.length; i++ ) {
			switch (cls[i]) {
				case 'color':
				case 'link':
				case 'page':
				case 'adminpage':
				case 'image':
				case 'document':
				case 'flash':
				case 'video':
				case 'date':
				case 'time':
				case 'thumbnail':
					return true;
			}
		}
		return this.getAttribute && this.getAttribute('title') ? true : false;
	};

	// Deselect anything highlighted.
	$.fn.unselect = function() {
		return this.each(function(i){
			var doc = this.ownerDocument;
			if ( doc.selection && doc.selection.createRange ) {
				var range = doc.selection.createRange();
				if ( range.text ) {
					doc.selection.empty();
				}
			} else {
				var wn = doc.defaultView || doc.parentWindow;
				if ( wn.getSelection ) {
					wn.getSelection().removeAllRanges();
				}
			}
			if (this.blur) { this.blur(); }
		});
	};

	// Search the website.
	$.widget( "cms.sitesearch", {
		options: {
		},

		// Set up the site search.
		_create: function() {
			this.options.path = this.element.attr('_path') || this.options.path;
			this.options.limit = this.element.attr('_limit') || this.options.limit;
			this.options.exclude = this.element.attr('_exclude') || this.options.exclude;
			this.options.key = this.element.attr('_key') || this.options.key;
			this.options.autosuggest = $.toBool( this.element.attr('_autosuggest') || this.options.autosuggest );

			// Set up the search box.
			var fn = $.proxy(this._search, this);
			this.elements = {
				input: this.element.is('input:text') ? this.element : this.element.find('input:text:first')
			};
			this.elements.input.toggleValue().bindkey('ENTER', fn);

			// And submit button.
			var button = this.element.find("input:button,input:submit,input:image,a[href*='__doPostBack']:not([onclick]),a[href*='WebForm_DoPostBackWithOptions']");
			if ( !button.length ) {
				button = this.element.find('img:last');
			}
			button.bind('click',fn);

			// Set up any onresults event.
			var onresults = this.element.attr('_onresults');
			if ( onresults ) {
				try {
					this.options.onresults = eval( '('+onresults+')' );
				} catch(ex) {;}
			}

			// Set up any autosuggest.
			if ( this.options.autosuggest) {
				this.elements.input.combobox({
					ajaxUrl: $.getAjaxUrl( window.location.href, {AutoSuggest:this.element.attr('id')} ),
					style: {
						panel: this.element.attr('_panel') || 'combo-box'
					},
					minchars: $.toInt(this.element.attr('_minchars'))||3,
					maxHeight: null,
					textField: this.element.attr('_textfield'),
					onbuild: function(wz){
						return function(results,combo){
							if ( wz.options.onresults && wz.options.onresults(results,combo,wz) === false ) {
								return false;
							}
						};
					}(this),
					onselect: function(wz){
						return function(e, selected){
							var text = selected.getRow()[this.options.textField];
							this.element.val( text || "" );
							if ( text ) {
								wz._search(e);
							}
							return false;
						}
					}(this)
				});
			}
		},

		// Redirect to the site search.
		_search: function(e) {
			// Get the search criteria.
			var params = {};
			params[ this.options.key || 'C' ] = this.elements.input.val();
			if ( this.options.limit ) {
				params.L = this.options.limit;
			}
			if ( this.options.exclude ) {
				params.X = this.options.exclude;
			}

			// Cancel the event.
			e.preventDefault();
			e.stopPropagation();
			e.stopImmediatePropagation();

			// Redirect the page.
			window.location.href = $.getAjaxUrl( this.options.path, params );
			return false;
		}
	});

	// Open up linked images as a lightbox.
	$.widget( "cms.lightbox", {
		options: {
			width: 550,
			height: 400
		},

		_html: '\
<div>\
	<div class="cms-lightbox">\
		<div class="cms-lightbox-image">\
			<div class="cms-lightbox-image1"></div>\
			<div class="cms-lightbox-image2"></div>\
		</div>\
		<div class="cms-lightbox-description"></div>\
		<div class="cms-lightbox-images"><div></div></div>\
		<div class="cms-lightbox-nav" _nav="-1"><div>&lt; Prev</div></div>\
		<div class="cms-lightbox-nav next" _nav="1"><div>Next &gt;</div></div>\
		<div class="cms-lightbox-panel" _panel="-1"><div>&laquo;</div></div>\
		<div class="cms-lightbox-panel next" _panel="1"><div>&raquo;</div></div>\
	</div>\
</div>',

		// Create the loading screen.
		_create: function() {
			// Get all links that point to images.
			var base = $(document.body).attr('_base'),
				images = [],
				preload = [],
				lightbox = this,
				w = $.toInt( this.element.attr('_lightwidth') ),
				h = $.toInt( this.element.attr('_lightheight') );

			if ( w && h ) {
				this.options.width = w;
				this.options.height = h;
			}

			// Initialize the lightbox elements -- links pointing to images.
			this.element
				.find("a[href]")
					.filter(function(e){
						var link = $(this);
						var href = link.attr('href').replace( base, "/" );
						if ( $.cms.lightbox.r_img.test(href) ) {
							link.data('index', images.length);
							var thumb = lightbox._getImageUrl(href),
								image = {
								src: href,
								thumb: thumb,
								css: 'url('+thumb+')',
								title: link.attr('_title'),
								description: link.attr('_description')
							};
							if ( image.description ) {
								lightbox.description = true;
							}
							images.push(image);
							preload.push(thumb);
							return true;
						} else {
							return false;
						}
					})
					.click( $.proxy(this._open, this) );

			// Preload the images.
			$.preload(preload);
			this.images = images;
		},

		// Open the lightbox when someone clicks on a link to an image.
		_open: function(e) {
			var link = $(e.target).closest('a');
			this.index = link.data('index');

			// Get the spacing for title/description.
			var padding = 60;
			if ( this.description ) {
				padding += 41;
			}

			if ( !this.popup ) {
				// Initialize the popup.
				this.popup = $(this._html)
					.children('div.cms-lightbox')
						.css({
							width: this.options.width,
							height: this.options.height+padding
						})
					.end()
					.dialog({
						modal: true,
						width: this.options.width+122,
						height: 'auto',
						open: $.proxy(this._openLightbox, this),
						beforeClose: $.proxy(this._closeLightbox, this)
					});
			} else {
				// Or just open it.
				this.popup.dialog('open');
			}

			// Cancel the default click event.
			return false;
		},

		// When the lightbox is opened.
		_openLightbox: function(e, data, popup) {
			if ( !this.elements ) {
				// Initialize the elements.
				var css = {width:this.options.width,height:this.options.height};
				this.elements = {
					main: popup.uiDialog.find('div.cms-lightbox').click( $.proxy(this._navigate, this) ),
					image: popup.uiDialog.find('div.cms-lightbox-image').css(css),
					image1: popup.uiDialog.find('div.cms-lightbox-image1').css(css),
					image2: popup.uiDialog.find('div.cms-lightbox-image2').css(css),
					title: popup.uiDialog.find('div.ui-dialog-titlebar>span'),
					description: popup.uiDialog.find('div.cms-lightbox-description'),
					images: popup.uiDialog.find('div.cms-lightbox-images').css({width:this.options.width+71}).children('div'),
					nav: popup.uiDialog.find('div.cms-lightbox-nav')
							.bind('mouseenter mouseleave', $.proxy(this._showNav, this) )
							.unselectable(),
					panel: popup.uiDialog.find('div.cms-lightbox-panel')
				};

				if ( this.options.height != 400 ) {
					var padding = ( ( this.options.height - 400 ) / 2 ) + 150;
					this.elements.nav.css({paddingTop:padding,paddingBottom:padding});
				}


				if ( !this.options.nav ) {
					// Get the color of the nav elements.
					var nav = this.elements.nav.children().eq(0);
					this.options.nav = {
						color: nav.css('color'),
						backgroundColor: nav.css('backgroundColor'),
						borderLeftColor: nav.css('borderLeftColor'),
						borderRightColor: nav.css('borderRightColor'),
						borderBottomColor: nav.css('borderBottomColor'),
						borderTopColor: nav.css('borderTopColor')
					};
				}
				if ( !this.options.bg ) {
					// And the color of the panel background.
					var bg = this.elements.main.css('backgroundColor');
					this.options.bg = {
						color: bg,
						backgroundColor: bg,
						borderLeftColor: bg,
						borderRightColor: bg,
						borderBottomColor: bg,
						borderTopColor: bg
					};
				}
				// Set the default state of the nav elements.
				this.elements.nav.children().css(this.options.bg).css({display:'block'});

				// Hide the description panel if we don't need it.
				if ( !this.description ) {
					this.elements.description.hide();
				}

				// Set the paging state.
				this.paging = {
					perpage: Math.max( Math.floor((this.options.width+78)/57), 1),
					page: 0
				};

				// We don't need panel navigation if everything fits in one panel.
				if ( this.paging.perpage > this.images.length ) {
					this.elements.panel.hide();
				}

				// Add the tiny thumbnails at the bottom.
				var sb = [];
				for ( var i=0; i<this.images.length; i++ ) {
					var url = this._getImageUrl( this.images[i].src, 50, 50 );
					sb.push('<div style="background-image:url(' + url + ')" _index="' + i + '"></div>');
				}
				this.elements.images.html( sb.join('') );
			}
			// Set the image and keydown event.
			this._setImage();
			this._markThumbnail();
			$(document).bindkey(true, $.proxy(this._navigate, this) );
		},

		// Unbind the keydown event.
		_closeLightbox: function(e, data, popup) {
			$(document).unbindkey(true, this._navigate);
		},

		// Set the active image.
		_setImage: function() {
			// Show the image preview.
			var image = this.images[this.index];
			this.elements.image1.css({ backgroundImage: image.css , opacity: 1 });
			this.elements.image2.css({ backgroundImage: 'none' });
			this.elements.title.html( image.title || "" );
			this.elements.description.html( image.description || "" );
		},

		_markThumbnail: function() {
			// If we need to move the paging panel.
			var page = Math.floor(this.index / this.paging.perpage);
			if ( page != this.paging.page ) {
				this.paging.page = page;
				var left = ( 0 - page ) * this.paging.perpage * 57;
				this.elements.images.stop().animate({marginLeft:left});
			}

			// Mark the little thumbnail as active.
			this.elements.images
					.children()
						.filter('.active')
							.removeClass('active')
						.end()
						.eq( this.index )
							.addClass('active');
		},

		// When hovering over the nav area, show it.
		_showNav: function(e) {
			var nav = $(e.target).closest('div.cms-lightbox-nav').children('div').stop();
			if ( e.type === 'mouseenter' ) {
				nav.animate(this.options.nav, 300, 'linear' );
			} else {
				nav.animate(this.options.bg,  300, 'linear' );
			}
		},

		// Nativate forwards and backwards.
		_navigate: function(e) {
			var amount;
			if ( e.type === 'keydown' ) {
				switch ( e.which ) {
					case 39:
						this._advance( this.index + 1 );
						break;
					case 37:
						this._advance( this.index - 1 );
						break;
					default:
						return;
				}
			} else if ( e.type === 'click' ) {
				var el = $(e.target),
					p = el.parent(),
					index = el.attr('_index'),
					panel = $.toInt( el.attr('_panel')||p.attr('_panel') ),
					nav = $.toInt( el.attr('_nav')||p.attr('_nav') );

				if ( index ) {
					this._advance( $.toInt(index) );
				} else if ( nav ) {
					this._advance( this.index + nav );
				} else if ( panel ) {
					this._advance( (this.paging.page+panel)*this.paging.perpage );
				}
			}
		},

		// Advance to the specified image.
		_advance: function(index) {
			if ( index < 0 ) {
				index = this.images.length-1;
			} else if ( index >= this.images.length ) {
				index = 0;
			}
			this.prev = this.index;
			this.index = index;

			// Blank out the title and description at the start of the animation.
			this.elements.title.html( "" );
			this.elements.description.html( "" );
			this._markThumbnail();

			this.elements.image1
				.stop()
				.css({opacity:1})
				.css({ backgroundImage: this.images[this.prev].css })
				.animate({opacity:0}, 300, 'linear');
			this.elements.image2
				.stop()
				.css({opacity:0})
				.css({ backgroundImage: this.images[this.index].css })
				.animate({opacity:1}, 300, 'linear', $.proxy(this._setImage, this));
		},

		// Get the preview image url.
		_getImageUrl: function(src, w, h) {
			if ( src ) {
				return '/images/thumbnail.aspx?I='+$.encode(src)+'&W='+(w||this.options.width)+'&H='+(h||this.options.height)+'&B1=FFFFFF';
			} else {
				return null;
			}
		},

		// Remove any popup element.
		destroy: function() {
			if ( this.popup ) {
				this.popup.remove();
			}
			$.Widget.prototype.destroy.apply( this, arguments );
		}
	});

	$.cms.lightbox.r_img = /\.(?:gif|bmp|png|jpg|jpe|jpeg|tif|tiff)$/i;

	// Popup window to another url.
	$.widget( "cms.popup", $.ui.dialog, {
		options: {
			single: false,
			nobridge: true,
			modal: true,
			random: false,
			noscroll: false,
			submit: false,
			submitName: 'SAVE',
			buttons: {
				"CLOSE" : function(e, widget) {
					widget.close.apply(widget, [e]);
				}
			},
			open: function(e, data, widget) {
				widget._open.apply(widget, [e, data]);
			}
		},

		_html: '\
	<div>\
		<div class="ui-dialog-main">\
			<iframe class="ui-dialog-iframe" src="/Shared/blank.html" frameborder=0 marginheight=0 marginwidth=0 {scroll}></iframe>\
		</div>\
	</div>',

		// Create the popup.
		_create: function() {
			if ( typeof this.options.width === 'number' ) {
				this.options.width += 2;
			}
			if ( typeof this.options.height === 'number' ) {
				this.options.height += 76;
			}

			if ( this.options.submit ) {
				this.options.buttons = {};
				this.options.buttons[ this.options.submitName || 'SAVE' ] = function(e, widget) { widget._submit.apply(widget, [e]); };
				this.options.buttons[ "CANCEL" ] = function(e, widget) { widget.close.apply(widget, [e]); };
			}

			// Run the base create.
			$.ui.dialog.prototype._create.apply( this, arguments );

			// If we have room, fix the wizard into position.
			var wn = $(window).dimensions();
			if ( !$.browser.msie6 && 
				 wn.width - this.options.width > 80 &&
				 wn.height - this.options.height > 80 ) {
				this.uiDialog.css({position:'fixed'});
			} else {
				this.uiDialog.css({position:'absolute'});
			}

			// Get the child elements.
			this.elements = {
				main: this.uiDialog.find('div.ui-dialog-content'),
				iframe: this.element.find('iframe')
			};

			// Add the overflow attribute on noscroll iframes for Webkit support.
			if ( this.options.noscroll ) {
				this.elements.iframe.css({
					overflow: 'hidden',
					overflowY: 'hidden',
					overflowX: 'hidden'
				});
			}

			// Do not let a mousewheel scroll propagate beyond the wizard itself.
			this.uiDialog.captureScroll();

			// Add the popup to the collection.
			if ( !$.cms.popup.list ) {
				$.cms.popup.list = [];
			}
			$.cms.popup.list.addItem(this);
		},

		// Open the popup.
		_open: function() {
			// See if we need to prevent caching.
			var url = this.options.random ? $.getAjaxUrl(this.options.url,{RND:Math.random()}) : this.options.url;

			this.elements.iframe.css({
				width: this.elements.main.width(),
				height: this.elements.main.height()
			})
			.attr('src', url);
		},

		_submit: function() {
			// Fire the close event.
			if ( $.isFunction(this.options.onsubmit) ) {
				// If the event explicitly returns false, stop now.
				if ( this.options.onsubmit.apply(this, arguments) === false ) {
					return false;
				}
			}

			// Do_something();
		},

		// Close the wizard.
		close: function() {
			// Fire the close event.
			if ( $.isFunction(this.options.onclose) ) {
				// If the event explicitly returns false, stop now.
				if ( this.options.onclose.apply(this, arguments) === false ) {
					return false;
				}
			}

			// Clear any pages loaded into the iframe.
			this.elements.iframe.unbind().attr('src','/Shared/blank.html');

			// Remove the popup from the collection.
			if ( $.cms.popup.list ) {
				$.cms.popup.list.removeItem(this);
			}

			// Call the base dialog close event.
			$.ui.dialog.prototype.close.apply( this, arguments );

			// Remove the popup from the DOM when the wizard is closed.
			this.element.remove();
		},

		// Dispose of resources.
		destroy: function() {
			return $.ui.dialog.prototype.destroy.apply( this, arguments );
		}

	});

	$.popup = function(options) {
		// Create the popup.
		var html = $.cms.popup.prototype._html.replace('{scroll}', options && options.noscroll ? 'scrolling=no' : '');
		var el = $(html);
		var instance = new $.cms.popup( options, el[0] );
		$.data( el[0], 'popup', instance );
		return instance;
	};

}


	// What is the current cursor position of the selected element?
	$.fn.cursorat = function() {
		if ( !this.length ) {
			return null;
		}
		var input = this[0];
		if ( input.setSelectionRange ) {
			return [input.selectionStart, input.selectionEnd];
			return pos;
		} else if ( input.createTextRange ) {
			input.focus();
			var r1 = input.ownerDocument.selection.createRange();
			var len = r1.text.length;
			var r2 = input.createTextRange();
			r2.setEndPoint("EndToStart", r1);
			return [ r2.text.length, r2.text.length + len ];
		} else {
			return null;
		}
		
	};

	// Select the contents of a control.
	$.fn.selecttext = function(start,end) {
		return this.each(function(i){
			// Focus on the element.
			if (this.focus) {
				this.focus();
			}

			if ( start === undefined ) {
				start = 0;
			} else if ( start === -1 ) {
				start = this.value.length;
			}

			if ( end === undefined ) {
				end = this.value.length;
			}

			if ( this.setSelectionRange ) {
				// Non-IE.
				this.setSelectionRange(start, end);
			} else if ( this.createTextRange ) {
				// IE input element.
				var range = this.createTextRange();
				range.collapse(true);
				range.moveStart("character", start); 
				range.moveEnd("character", end-start); 
				range.select();
			} else {
				// Get a reference to the document and window.			
				var doc = this.ownerDocument;
				var wn = doc.defaultView || doc.parentWindow;

				if ( doc && 
					 doc.body && 
					 doc.body.createTextRange ) {
					// IE non-input element.
					var range = doc.body.createTextRange();
					range.moveToElementText(this);
					range.select();
				} else if ( doc && 
							doc.createRange &&
							wn &&
							wn.getSelection ) {
					// Non-IE non-input element.
					var range = doc.createRange();
					range.selectNodeContents(this);
					var selection = wn.getSelection();
					selection.removeAllRanges();
					selection.addRange(range);
				}
			}
		});
	};

	// Insert text at the cursor position of a text field.
	$.fn.insertAtCursor = function(text) {
		return this.each(function(i){
			if ( typeof this.value === undefined || !text ) {
				return;
			}

			if ( this.setSelectionRange ) {
				// Mozilla.
				var start = this.selectionStart,
					end = this.selectionEnd,
					top = this.scrollTop;

				this.value = this.value.substring(0,start) + text + this.value.substring(end, this.value.length);
				this.selectionStart = start + text.length;
				this.selectionEnd = start + text.length;
				this.scrollTop = top;
			} else {
				// IE.
				var doc = this.ownerDocument;
				var wn = this.defaultView || doc.parentWindow;
				
				if ( doc &&
					 doc.selection &&
					 doc.selection.createRange ) {
					var range = doc.selection.createRange();
					range.text = text;
					range.collapse(true);
				}
			}
		});
	};

	// Scroll an element into position.
	$.fn.scrollIntoView = function(speed,top) {
		return this.each(function(i){
			// Get the element and its scroll parent.
			var $el = $(this);
			var parent = $el.parents().filter(function(){
				return /auto|scroll|hidden/.test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
			}).eq(0);

			// If we have a scrollable parent element.
			if ( parent.length ) {
				// And the parent needs to scroll.
				if ( parent[0].scrollHeight > parent[0].offsetHeight ) {
					var t = $el.offset().top - parent.offset().top + parent[0].scrollTop;
					var h1 = $el.outerHeight(true);
					var h2 = parent[0].clientHeight;

					var scrollTo = null;
					if ( top ) {
						// Put the element at the top of the scrollable area.
						scrollTo = Math.max(t - 10, 0);
					} else if ( t + h1 > parent[0].scrollTop + h2 - 10 ) {
						// If the bottom of the element is below the visible area.
						scrollTo = t + h1 + 10 - h2;
					} else if ( t < parent[0].scrollTop + 10 ) {
						// The top of the element is above the visible area.
						scrollTo = Math.max(t - 10, 0);
					}

					if ( scrollTo ) {
						if ( speed ) {
							parent.animate({scrollTop:scrollTo},speed);
						} else {
							parent[0].scrollTop = scrollTo;
						}
					}
				}
			} else {
				// Otherwise, set the document scrollbar so that the element is near the top of the visible part of the screen.
				var t = $el.offset().top - $.toInt( $(document.body).css('paddingTop') ) - 10;

				if ( speed ) {
					$('html,body').animate({scrollTop:t});
				} else {
					document.documentElement.scrollTop = t;
					document.body.scrollTop = t;
				}
			}
		});
	};

	// Set an element as unselectable.
	$.fn.unselectable = function() {
		if ( $.browser.webkit ) {
			this.css('-webkit-user-select', 'none');
		} else if ( $.browser.mozilla ) {
			this.css('-moz-user-select', 'none');
		} else {
			this.css('user-select', 'none').attr('unselectable','on');
		}
		return this.bind('selectstart.ui', function() { return false; });;
	};

	// Remove a named style from this collection of element.
	$.fn.removeStyle = function(name) {
		// Convert the style name to a lower-case string array.
		if (!name) {
			return this;
		} else if (name.toLowerCase) {
			name = [ name.toLowerCase() ];
		} else if ($.isArray(name)) {
			for (var i=0;i<name.length;i++)
				name[i] = (''+name[i]).toLowerCase();
		} else {
			return this;
		}

		// Remove the styles from each of the elements.
		return this.each(function(i) {
			var el = $(this);
			var styles = (el.attr('style')||'').split(';');
			
			for ( var i=styles.length-1; i>=0; i-- ) {
				var style = $.trim(styles[i].split(':')[0]).toLowerCase();
				for (var j=0;j<name.length;j++) {
					// If we found a matching style (or it matches a meta-style).
					if ( style === name[j] || style.indexOf(name[j]+'-') === 0 ) {
						// Remove it.
						if (i==0) {
							styles.shift();
						} else if (i==styles.length-1) {
							styles.pop();
						} else {
							styles.splice(i,1);
						}
						break;
					}
				}
			}

			// Assign the changed style.
			var newstyles = $.trim( styles.join(';') );
			if (newstyles) {
				el.attr('style',newstyles);
			} else {
				el.removeAttr('style');
			}
		});
	};

	// When pressing enter inside a form element, have it submit itself (rather than the first submit button on the whole page).
	$.fn.selfsubmit = function() {
		// Find the submit button for this form.  If there are multiple possible buttons, pick the last one.
		var btn = this.find("input:submit,input:image,a[href*='__doPostBack']:not([onclick]),a[href*='WebForm_DoPostBackWithOptions']").filter(':last');
		if (btn.length) {
			// If this is a search grid, wire up any "Clear" links.
			if ( this.is("[icobalt='CobaltControls.Controls.SearchGrid']") ) {
				this.find("a[href^='javascript:void'][href*='Clear']").data('submitbtn',btn).click(function(e){
					var btn = $(this).prev(':input').val("").trigger("change").end().data('submitbtn');
					if ( btn && btn.length ) {
						e.preventDefault();
						e.stopPropagation();
						e.stopImmediatePropagation();

						if ( btn[0].click ) {
							btn[0].click();
						} else if ( btn.is("a[href^='javascript:']") ) {
							window.location.href = btn.attr('href');
						} else {
							btn.click();
						}

						return false;
					}
				});
			}
			// Find all text elements in the form and bind the onenter event.
			this.find('input:text,input:password').data('submitbtn',btn).bind('keypress',function(e){
				if ( e.which !== 13 ) {
					return;
				}
				var btn = $(this).data('submitbtn');
				if (btn && btn.length) {
					e.preventDefault();
					e.stopPropagation();
					e.stopImmediatePropagation();

					if ( btn[0].click ) {
						btn[0].click();
					} else if ( btn.is("a[href^='javascript:']") ) {
						window.location.href = btn.attr('href');
					} else {
						btn.click();
					}

					return false;
				}
			});
		}
		return this;
	};

	// Handle keypresses on a phone control.
	$.fn.autoNumber = function() {
		var inputs = $(this).find('input:text');
		var total = inputs.length;
		inputs.each(function(i){
			this._index = i;
			this._last = total-1;
			$(this).bind('keydown', $.autoNumber);
		});
		return this;
	};

	$.autoNumber = function(e) {
		var input = $(this);
		if ( ( e.which >= 48 && e.which <= 57 ) || ( e.which >= 96 && e.which <= 105 ) ) {
			// Typed a top-row number or a keypad-number.
			if ( e.shiftKey ) {
				// Can't type a shift-number.
				return false;
			} else if ( this._index < this._last ) {
				var max = $.toInt(input.attr('maxlength'));
				// If we've just typed the last number of a textbox.
				if ( this.value && this.value.length === max - 1 ) {
					// Get the next tab.
					var next = input.closest('table').find('input:text').eq( this._index + 1 );
					if ( next.length ) {
						var fn = function(input){
							return function(){
								input.focus();
								input = null;
							};
						}(next[0]);
						// Focus on a timeout to allow this event to finish first.
						setTimeout(fn,1);
					}
				}
			}
		} else if ( e.which === 9 ) {
			// See if we're tabbing off a tab when it's blank (except for the first textbox).
			if ( !e.shiftKey && !this.value && this._index > 0 && this._index < 3 ) {
				var prev = input.closest('table').find('input:text').eq( this._index - 1 );
				if ( prev.val() ) {
					// If the previous box has a value, cancel the tab.
					return false;
				}
			}
		} else {
			switch (e.which) {
				case 8:		// BACKSPACE
				case 27:	// ESCAPE
				case 35:	// END
				case 36:	// HOME
				case 37:	// LEFT
				case 39:	// RIGHT
				case 46:	// DELETE
					// Allow these keypresses.
					return true;
				default:
					// Everything else is invalid.
					return false;
			}
		}
	};

	// Prevent paste in a control.
	$.fn.nopaste = function() {
		return this.bind('keydown contextmenu', function(e){
			var cancel = false;
			switch ( e.type ) {
				case 'keydown':
					if ( ( e.ctrlKey || e.cmdKey ) && e.which === 86 ) {
						cancel = true;
					}
					break;
				case 'contextmenu':
					cancel = true;
					break;
			}
		
			if ( cancel ) {
				if ( e.preventDefault ) {
					e.preventDefault();
					e.stopPropagation();
					e.stopImmediatePropagation();
				}
				return false;
			}
		});
	};

	// Deserializes row data to an object.
	$.fn.getRow = function(attr) {
		if (!this.length)
			return {};

		// And its row.
		var row = this[0].$row;
		if (!row)
		{
			// Initialize it if it doesn't exist already.
			try { row = eval('('+this.attr(attr||'_row')+')') }
			catch(ex) { row = {}; }
			this[0].$row = row = row||{};
		}

		// Return the row.
		return row;
	};

	// 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); }

		var imgs = $.makeArray(img);
		for (var i=0;i<imgs.length;i++)
		{
			// Add the image to the preloader if it isn't already there.
			var img = imgs[i];
			if (preloader.find("img[src$='"+img+"']").length==0)
				preloader.append('<img src="'+img+'">');
		}
	};

	// Encode a string for safe storage in a text field, as well as save for form/querystring submission to the server.
	$.specRegex = /['"\(\)]/g;
	$.encode = function(val) {
		if ( val === undefined || val === null ) {
			return '';
		} else {
			return encodeURIComponent(val+"").replace($.specRegex,function(m){
				switch (m) {
					case "'":
						return '%27';
					case '"':
						return '%22';
					case '(':
						return '%28';
					case ')':
						return '%29';
					default:
						return m;
				}
			});
		}
	};

	// Decode an encoded string.
	$.decode = function(val) {
		if ( !val ) {
			return "";
		} else {
			try {
				return decodeURIComponent(val);
			} catch (ex) {
				return val || "";
			}
		}
	};

	// Encode just the key characters necessary to make a save url querystring value.
	$.encodeUri = function(val) {
		if (!val) {
			return '';
		} else {
			return val.replace(/[?& =]/g,function(m){
				switch(m){
					case '?':
						return '%3F';
					case '&':
						return '%26';
					case ' ':
						return '%20';
					case '=':
						return '%3D';
					default:
						return m;
				}
			});
		}
	};

	// Bookmark a link.
	$.bookmark = function(obj, title, url){
		if ( window.sidebar && window.sidebar.addPanel ) {
			// Mozilla Firefox Bookmark
			window.sidebar.addPanel( title, url,"" );
			return false;
		} else if ( window.external && 'addFavorite' in window.external ) {
			// IE Favorite
			window.external.AddFavorite( url, title );
		} else if ( window.opera && window.print ) {
			//Opera Hotlist
			obj.setAttribute('href',url);
			obj.setAttribute('title',title);
			obj.setAttribute('rel','sidebar');
			obj.click();
			return false;
		} else {
			window.location.href = url;
			return false;
		}
	};

	// Log something to the console..
	$.log = function(obj) {
		if (typeof firebug != 'undefined')
			firebug.d && firebug.d.console && firebug.d.console.cmd && firebug.d.console.cmd.log(obj);
		else if (typeof console !='undefined')
			console && console.log && console.log(obj);
	};

	// Trace the current stack.
	$.trace = function() {
		if (typeof firebug != 'undefined')
			firebug.d && firebug.d.console && firebug.d.console.cmd && firebug.d.console.cmd.trace();
		else if (typeof console !='undefined')
			console && console.trace && console.trace();
	};

	// Inspect an element in firebug.
	$.inspect = function(el) {
		if (el && el.jquery)
			el = el.length ? el[0] : null;
		if (!el) return;
		if (typeof firebug != 'undefined')
			firebug.inspect && firebug.inspect(el);
	};

	// Capture a mouse-scroll.
	$.fn.captureScroll = function() {
		return this.bind('mousewheel', $.containScrolling);
	};

	// Stop a mousewheel scroll from propagating upwards.
	$._r_scrolling = /^(?:auto|scroll)$/;
	$.containScrolling = function(e) {
		// Get the target.
		var p = e.target;
		if ( e.target.nodeName.toLowerCase() === 'select' ) {
			return;
		}

		do {
			// If the current target is scrollable.
			if ( $._r_scrolling.test($.curCSS(p, 'overflow')) || $._r_scrolling.test($.curCSS(p, 'overflow-y')) ) {

				// Scroll the element.
				var top = p.scrollTop;
				p.scrollTop -= (e.delta*70);

				if ( e.target.nodeName.toLowerCase() === 'textarea' && top === p.scrollTop ) {
					// If we tried to scroll a textarea, but nothing happened, propagate this event up.
					continue;
				} else {
					return false;
				}
			}
		}
		// Until we reach the mouse-wheel bound element.
		while ( p != this && ( p = p.parentNode ) );

		// If we've reached no scrollable element, kill the event anyway.
		return false;
	};

	// Subscribe to a comet channel.
	$.comet = function(id, onupdate, onerror) {
		if ( id && onupdate === false ) {
			// Note that we've already unsubscribed.
			if ( window._cometunload ) {
				window._cometunload[id] = false;
			}

			// If we've been asked to cancel the comet subscription.
			$.ajax({
				url: $.getAjaxUrl('/CobaltComet.ashx', {ID:id,Cancel:'true',RND:Math.random()})
			});
			return;
		}

		// If there's no id or onupdate function, this comet call will take up resources for no reason.
		if ( !id || !$.isFunction(onupdate) ) {
			return;
		}

		// Note that we've subscribed to this channel.
		if ( !window._cometunload ) {
			window._cometunload = {};
			$(window).unload($.cometunload);
		}
		window._cometunload[id] = true;

		// Fire an ajax call.
		$.ajax({
			url: $.getAjaxUrl('/CobaltComet.ashx', {ID:id,RND:Math.random()}),
			dataType: 'json',
			success: function(data){
				// If we have data, process it.
				if ( data && data !== 'TIMEOUT' ) {
					onupdate(data);
				}

				// As long as we didn't make an invalid comet call, restart it.
				if ( data && data !== 'INVALID' ) {
					$.comet(id, onupdate, onerror);
				} else {
					// We have an invalid comet call, so we'll unsubscribe right away.
					window._cometunload[id] = false;
				}
			},
			error: function(xhr){
				// If we have an error function, run it.
				if ( $.isFunction(onerror) ) {
					// If this error function expressly returns false, we'll stop the comet subscription.
					if ( onerror(xhr) === false ) {
						return;
					}
				}

				// Restart the comet call.
				$.comet(ds, onupdate, onerror);
			}
		});
	};

	// When the page is unloaded, kill off any open comet connections.
	$.cometunload = function() {
		if ( window._cometunload ) {
			for ( var p in window._cometunload ) {
				if ( window._cometunload[p] ) {
					$.comet(p,false);
				}
			}
		}
	};

	// 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).
		var pos = 0;
		if (!character) character = " ";
		while (pos<value.length && value.charAt(pos) === character) { pos++; }

		// Return the trimmed string.
		return value.substring(pos);
	};

	// Return a text value, parsed as an integer.  Return any NaN as 0.
	$.toInt = function(amount) {
		if (!amount) {
			return 0;
		} else if ( typeof amount === 'number' ) {
			return Math.round(amount);
		} else 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;
		}
	};

	// Return a text value, parsed as a float.  Return any NaN as 0.
	$.toFloat = function(amount) {
		if (!amount) {
			return 0.00;
		}
		var factor = 1.00;
		if ( typeof amount === 'number' ) {
			return amount;
		} else if ( typeof amount === 'string' ) {
			if (/%\s*$/.test(amount)) {
				factor = 0.01;
			}
			amount = amount.replace(/[^\d.-]/g,'');
		}
		var val = parseFloat(amount) * factor;
		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 ( val === undefined || !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;
		}
	};

	// Convert data to an int array.
	$.toIntArray = function(val) {
		if ( !val ) {
			return [];
		}
		if ( typeof val === 'string' ) {
			if ( val.indexOf('|') >= 0 ) {
				val = val.split('|');
			} else if ( val.indexOf('^') >= 0 ) {
				val = val.split('^');
			} else {
				val = val.split(',');
			}
		} else if ( !$.isArray(val) ) {
			val = $.makeArray(val);
		}
		for ( var i=0; i<val.length; i++ ) {
			val[i] = $.toInt(val[i]);
		}
		return val;
	};

	// Convert an RGB color array to a hex-based color.
	$.toHex = function(color) {
		if ( !color || !$.isArray(color) || color.length != 3 || typeof color[0] != 'number' ) {
			return "000000";
		}
		try {
			var hex = 
				(color[0]<16?'0':'')+color[0].toString(16)+
				(color[1]<16?'0':'')+color[1].toString(16)+
				(color[2]<16?'0':'')+color[2].toString(16);
			return hex.toUpperCase();
		}
		catch(ex) {
			return "000000";
		}
	};

	// Convert an RGB color to a Hue/Saturation/Value color.
	$.RGBtoHSV = function(rgb) {
		if ( !rgb || !$.isArray(rgb) || rgb.length != 3 || typeof rgb[0] != 'number' ) {
			return [ 0, 0, 0 ];
		}

		// Get the color parts.
		var red = rgb[0];
		var green = rgb[1];
		var blue = rgb[2];

		// Analyze the min/max.
		var minRGB = Math.min(Math.min(red,green),blue);
		var maxRGB = Math.max(Math.max(red,green),blue);
		var delta = maxRGB-minRGB;

		// Declare the HSV variables.
		var h = 0.0;
		var v = maxRGB / 255;
		var s = maxRGB==0 ? 0 : delta / maxRGB;

		// Calculate the hue.
		if (s!=0) {
			if (maxRGB == red) {
				h = (green-blue)/delta;
			} else if (maxRGB == green) {
				h = 2 + (blue-red)/delta;
			} else {
				h = 4 + (red-green)/delta;
			}
		} else {
			h = 0;
		}

		// Convert the hue to degrees.
		h = h * 60;
		if ( h < 0 ) {
			h += 360;
		}

		return [ h, s, v ];
	};

	// Convert an RGB color to a Hue/Saturation/Value color.
	$.HSVtoRGB = function(hsv) {
		if ( !hsv || !$.isArray(hsv) || hsv.length != 3 || typeof hsv[0] != 'number' ) {
			return [ 0, 0, 0 ];
		}
		
		// Get the color parts.
		var hue = hsv[0];
		var saturation = hsv[1];
		var value = hsv[2];

		if ( saturation === 0 ) {
			// Greyscale.
			value = parseInt(value*255);
			return [ value, value, value];
		} else {
			// Divide the hue into 6 segments of the circle.
			hue = hue/60;
			var i = Math.floor(hue);

			// Calculate other color values.
			var f = hue-i;
			var p = value * ( 1 - saturation );
			var q = value * ( 1 - saturation * f);
			var t = value * ( 1 - saturation * ( 1 - f ) );

			// Calculate RGB.
			var r,g,b;
			switch (i)
			{
				case 0:
					r = value;
					g = t;
					b = p;
					break;
				case 1:
					r = q;
					g = value;
					b = p;
					break;
				case 2:
					r = p;
					g = value;
					b = t;
					break;
				case 3:
					r = p;
					g = q;
					b = value;
					break;
				case 4:
					r = t;
					g = p;
					b = value;
					break;
				default:
					r = value;
					g = p;
					b = q;
					break;
			}
		}

		// Return RGB as an array.
		return [ parseInt(r*255), parseInt(g*255), parseInt(b*255) ];
	};

	// 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;
	};

	// Format a number with digit grouping.
	$.numberFormat = function(amount,digits) {
		// Get the property amount and digits.
		amount = $.toFloat(amount);
		digits = $.toInt(digits);

		// For amounts 1000 and above, digit group by thousands.
		var sb = [];
		var scale = Math.floor( Math.log(amount) / Math.LN10 / 3 );
		while ( scale > 0 ) {
			var factor = Math.pow(10, scale*3);
			sb.push( Math.floor(amount/factor) );
			amount = amount % factor;
			scale--;
		}

		// Parse the final segment.
		if ( digits > 0 ) {
			// If we have digits, render them.
			var factor = Math.pow(10,digits);
			var number;
			if ( amount < 1 ) {
				// Ensure fractional numbers less than 0 have a zero prefix.
				number = ( ''+Math.floor( (10+amount)*factor ) ).substr(1);
			} else {
				// Convert the number to a string.
				number = ''+Math.floor(amount*factor);
			}
			// Parse out the factor.
			sb.push( number.substr(0,number.length-digits) + '.' + number.substr(number.length-digits) );
		} else {
			if ( amount < 100 && sb.length > 0 ) {
				sb.push( ( ''+Math.floor(1000+amount) ).substr(1) );
			} else {
				sb.push( Math.floor(amount) );
			}
		}

		return sb.join(',');
	};

	// Format a number with percent formatting.
	$.percentFormat = function(amount,digits) {
		// Get the property amount and digits.
		amount = $.toFloat(amount) * 100;
		return $.numberFormat(amount,digits) + '%';
	};

	// Format a file size in bytes.
	$.kbFormat = function(bytes,upper) {
		bytes = $.toInt(bytes);
		if (bytes<1024)
			return bytes+(upper?' B':'b');
		else if (bytes<(1024*1024))
			return (Math.round(bytes/1024*10)/10)+(upper?' KB':'kb');
		else
			return (Math.round(bytes/1024/1024*10)/10)+(upper?' MB':'mb');
	};

	// Subset of datetime function.
	$.datetime = {
		pattern: /(d+)|(h+)|(H+)|(m+)|(M+)|(s+)|(t+)|(T+)|(y+)|(rr)|(RR)/g,
		// Add a zero if the number is less than 10.
		leadingZero: function(val) {
			if (val<10)
				return '0'+val;
			else
				return val;
		},

		// Format the data with .NET pattern matches.
		format: function(date,format) {
			if (!date) return '';
			if (!format) format = 'd-MMM-yyyy';
			return format.replace($.datetime.pattern,function() {
				return $.datetime.process(date, Array.prototype.slice.call( arguments, 1 ));
			});
		},

		// Process the formatting.
		process: function(dateTime,args) {
			for (var i=0;i<args.length;i++)
			{
				// Iterate through the arguments and find which element of the datetime pattern was matched.
				var key = args[i];
				if (!key) continue;
				switch (i)
				{
					case 0:
						switch (key.length)
						{
							case 1:
								return dateTime.getDate();
							case 2:
								return $.datetime.leadingZero(dateTime.getDate());
							case 3:
								switch (dateTime.getDay())
								{
									case 0:
										return 'Sun';
									case 1:
										return 'Mon';
									case 2:
										return 'Tue';
									case 3:
										return 'Wed';
									case 4:
										return 'Thu';
									case 5:
										return 'Fri';
									case 6:
										return 'Sat';
									default:
										return '';
								}
							default:
								switch (dateTime.getDay())
								{
									case 0:
										return 'Sunday';
									case 1:
										return 'Monday';
									case 2:
										return 'Tuesday';
									case 3:
										return 'Wednesday';
									case 4:
										return 'Thursday';
									case 5:
										return 'Friday';
									case 6:
										return 'Saturday';
									default:
										return '';
								}
						}
					case 1:
						var hours = dateTime.getHours();
						if (hours>12) hours-=12;
						return key.length==1 ? hours||12 : $.datetime.leadingZero(hours||12);
					case 2:
						return key.length==1 ? dateTime.getHours() : $.datetime.leadingZero(dateTime.getHours());
					case 3:
						return key.length==1 ? dateTime.getMinutes() : $.datetime.leadingZero(dateTime.getMinutes());
					case 4:
						switch (key.length)
						{
							case 1:
								return dateTime.getMonth()+1;
							case 2:
								return $.datetime.leadingZero(dateTime.getMonth()+1);
							case 3:
								switch (dateTime.getMonth()+1)
								{
									case 1:
										return "Jan";
									case 2:
										return "Feb";
									case 3:
										return "Mar";
									case 4:
										return "Apr";
									case 5:
										return "May";
									case 6:
										return "Jun";
									case 7:
										return "Jul";
									case 8:
										return "Aug";
									case 9:
										return "Sep";
									case 10:
										return "Oct";
									case 11:
										return "Nov";
									case 12:
										return "Dec";
									default:
										return '';
								}
							default:
								switch (dateTime.getMonth()+1)
								{
									case 1:
										return "January";
									case 2:
										return "February";
									case 3:
										return "March";
									case 4:
										return "April";
									case 5:
										return "May";
									case 6:
										return "June";
									case 7:
										return "July";
									case 8:
										return "August";
									case 9:
										return "September";
									case 10:
										return "October";
									case 11:
										return "November";
									case 12:
										return "December";
									default:
										return '';
								}
						}
					case 5:
						return key.length==1 ? dateTime.getSeconds() : $.datetime.leadingZero(dateTime.getSeconds());
					case 6:
						return (dateTime.getHours()<12 ? 'a' : 'p') + (key.length==1 ? '' : 'm');
					case 7:
						return (dateTime.getHours()<12 ? 'A' : 'P') + (key.length==1 ? '' : 'M');
					case 8:
						return key.length==2 ? dateTime.getFullYear().toString().substr(2) : dateTime.getFullYear();
					case 9:
						var d = dateTime.getDate();
						switch (date.Day) {
							case 1:
							case 21:
							case 31:
								return "st";
							case 2:
							case 22:
								return "nd";
							case 3:
							case 23:
								return "rd";
							default:
								return "th";
						}
					case 10:
						var d = dateTime.getDate();
						switch (date.Day) {
							case 1:
							case 21:
							case 31:
								return "ST";
							case 2:
							case 22:
								return "ND";
							case 3:
							case 23:
								return "RD";
							default:
								return "TH";
						}
				}
			}
		},

		// An array of regex date patterns.
		_patterns: [
			/^(\d\d?)[ -\/\\]+([a-zA-Z]{3,9})[- \/\\]+(\d{2}|\d{4})$/,
			/^(\d{4})(\d{2})(\d{2})$/,
			/^(\d\d?)[ -\/\\]+([a-zA-Z]{3,9})[- \/\\]+(\d{2}|\d{4})\s+(\d{1,2}):(\d{2})(A|P)M?$/i,
			/^(\d{1,2}):(\d{2}):?(A|P)M?$/i
		],

		// A dictionary of month names.
		_months: {
			jan: 0,
			feb: 1,
			mar: 2,
			apr: 3,
			may: 4,
			jun: 5,
			jul: 6,
			aug: 7,
			sep: 8,
			oct: 9,
			nov: 10,
			dec: 11
		},

		// Parse a string into a datetime.
		parse: function(val) {
			if ( !val ) {
				// No value.
				return null;
			} else if ( val.constructor === Date ) {
				// It's already a date.
				return val;
			} else if ( typeof val === 'number' ) {
				// We'll assume that a number is ticks, convert to a date.
				return new Date(val);
			} else {
				// Clean up the value.
				val = $.trim((""+val)).replace(/(\d)([AP]M)\b/i,'$1:$2');
				// Try and parse it using native methods.
				var date = new Date( val );
				// If not, we'll do a regex match.
				if ( isNaN(date) ) {
					var p = $.datetime._patterns,
						r,
						index = -1,
						day,
						month,
						year,
						hours,
						mins,
						date = null;

					// Find a matching pattern.
					for ( var i=0; i<p.length; i++ ) {
						r = p[i].exec(val);
						if ( r ) {
							index = i;
							break;
						}
					}

					// Parse the date according to the pattern.
					switch (index) {
						case 0:
							day = r[1]-0;
							year = (r[3].length==2?'20'+r[3]:r[3])-0;
							month = $.datetime._months[r[2].substr(0,3).toLowerCase()]||0;
							date = new Date(year,month,day);
							break;
						case 1:
							date = new Date(parseInt(r[1]),parseInt(r[2])-1,parseInt(r[3]));
							break;
						case 2:
							day = r[1]-0;
							year = (r[3].length==2?'20'+r[3]:r[3])-0;
							month = $.datetime._months[r[2].substr(0,3).toLowerCase()]||0;
							date = new Date(year,month,day);
							hours = $.toInt(r[4])+(r[6].toUpperCase()=='P'?12:0);
							date.setHours(hours);
							date.setMinutes($.toInt(r[5]));
							date.setSeconds(0);
							break;
						case 3:
							date = new Date(1900,0,1);
							hours = $.toInt(r[1]);
							if (hours==12) hours = 0;
							hours +=(r[3].toUpperCase()=='P'?12:0);
							date.setHours(hours);
							date.setMinutes($.toInt(r[2]));
							date.setSeconds(0);
							break;
					}
				}
				return date;
			}
		}
	};

	// Format a phone number.
	$.phoneFormat = function(data) {
		if ( !data ) {
			return null;
		} else {
			var phone = (""+data).replace(/\D+/g,"");
			var start = "";
			if ( phone.substr(0,1) === "1" ) {
				start = "1";
				phone = phone.substr(1);
			}
			if ( phone.length >= 10 ) {
				return start + "(" + phone.substr(0,3) + ") " +
					phone.substr(3,3) + "-" + 
					phone.substr(6,4) + 
					( phone.length === 10 ? "" : " x"+phone.substr(10) );
			} else if ( phone.length >= 3 ) {
				return start + phone;
			} else {
				return data;
			}
		}
	};

	$.isDate = function(val) {
		return val && val.constructor === Date ? true : false;
	};

	// Setup a lookup for escape codes.
	$.escapeCodes = {};
	$.escapeCodes[ String.fromCharCode(8364) ] = '&euro;';
	$.escapeCodes[ String.fromCharCode(162) ] = '&cent;';
	$.escapeCodes[ String.fromCharCode(163) ] = '&pound;';
	$.escapeCodes[ String.fromCharCode(165) ] = '&yen;';
	$.escapeCodes[ String.fromCharCode(164) ] = '&curren;';
	$.escapeCodes[ String.fromCharCode(169) ] = '&copy;';
	$.escapeCodes[ String.fromCharCode(174) ] = '&reg;';
	$.escapeCodes[ String.fromCharCode(8482) ] = '&trade;';
	$.escapeCodes[ String.fromCharCode(177) ] = '&plusmn;';
	$.escapeCodes[ String.fromCharCode(8800) ] = '&ne;';
	$.escapeCodes[ String.fromCharCode(8776) ] = '&asymp;';
	$.escapeCodes[ String.fromCharCode(8804) ] = '&le;';
	$.escapeCodes[ String.fromCharCode(8805) ] = '&ge;';
	$.escapeCodes[ String.fromCharCode(247) ] = '&divide;';
	$.escapeCodes[ String.fromCharCode(215) ] = '&times;';
	$.escapeCodes[ String.fromCharCode(8734) ] = '&infin;';
	$.escapeCodes[ String.fromCharCode(189) ] = '&frac12;';
	$.escapeCodes[ String.fromCharCode(188) ] = '&frac14;';
	$.escapeCodes[ String.fromCharCode(190) ] = '&frac34;';
	$.escapeCodes[ String.fromCharCode(178) ] = '&sup2;';
	$.escapeCodes[ String.fromCharCode(179) ] = '&sup3;';
	$.escapeCodes[ String.fromCharCode(8240) ] = '&permil;';
	$.escapeCodes[ String.fromCharCode(182) ] = '&para;';
	$.escapeCodes[ String.fromCharCode(167) ] = '&sect;';
	$.escapeCodes[ String.fromCharCode(945) ] = '&alpha;';
	$.escapeCodes[ String.fromCharCode(946) ] = '&beta;';
	$.escapeCodes[ String.fromCharCode(947) ] = '&gamma;';
	$.escapeCodes[ String.fromCharCode(916) ] = '&Delta;';
	$.escapeCodes[ String.fromCharCode(181) ] = '&micro;';
	$.escapeCodes[ String.fromCharCode(937) ] = '&omega;';
	$.escapeCodes[ String.fromCharCode(8721) ] = '&sum;';
	$.escapeCodes[ String.fromCharCode(216) ] = '&Oslash;';
	$.escapeCodes[ String.fromCharCode(8736) ] = '&ang;';
	$.escapeCodes[ String.fromCharCode(186) ] = '&deg;';
	$.escapeCodes[ String.fromCharCode(171) ] = '&laquo;';
	$.escapeCodes[ String.fromCharCode(187) ] = '&raquo;';
	$.escapeCodes[ String.fromCharCode(183) ] = '&middot;';
	$.escapeCodes[ String.fromCharCode(8226) ] = '&bull;';
	$.escapeCodes[ String.fromCharCode(8224) ] = '&dagger;';
	$.escapeCodes[ String.fromCharCode(8225) ] = '&Dagger;';

	$.escapeCodes2 = {};
	$.escapeCodes2[ String.fromCharCode(8216) ] = '&lsquo;';
	$.escapeCodes2[ String.fromCharCode(8217) ] = '&rsquo;';
	$.escapeCodes2[ String.fromCharCode(8220) ] = '&ldquo;';
	$.escapeCodes2[ String.fromCharCode(8221) ] = '&rdquo;';

	// Clean up nonstandard html.
	$.cleanHtml = function( html, indent, strict ) {
		// If we don't have anything, return blank.
		if (!html) return '';

		// Build the escape regex to fix characters that need to be replaced with HTML escape codes.
		if ( !$.escapeRegex ) {
			var sb = [];
			for ( var p in $.escapeCodes ) {
				sb.push(p);
			}
			for ( var p in $.escapeCodes2 ) {
				sb.push(p);
			}
			$.escapeRegex = new RegExp("["+sb.join("")+"]",'g');
		}

		// HTML cleanup regexes.
		var rclean1 = new RegExp('\\s+(?:jQuery\\d+|sizcache\\d*|sizset\\d*)\\s*=\\s*"[^"]*"','g');
		//var rclean1a= new RegExp('&nbsp;','g');
		var rclean1b= new RegExp('<div [^>]*?_firebugConsole\\b[^>]*>[^<]*</div>','gi');
		var rclean2 = new RegExp('</?\\w+\\b','g');
		var rclean3a= new RegExp('<\\w[^>]*?>','g');
		var rclean3b= new RegExp('([\\w-\\$]+)\\s*=\\s*(?:([\'""])([\\s\\S]*?)\\2|([^\\s\'"">]+))','g');
		var rclean4 = new RegExp('"','g');
		var rclean5 = new RegExp(' style\\s*=\\s*"([^"]*)"','gi');
		var rclean5a= new RegExp('<base\\b[^>]+?\\bhref\\b[^>]+>\\s+','i');
		//var rclean5b= new RegExp('&nbsp;','g');
		var rclean5c= new RegExp('<span[^>]*?\\bscaytid\\b[^>]*>([^<]*)</span>','gi');
		var rclean5d= new RegExp('(<div [^>]*?class="[^>"]*?\\bbuildflash\\b[^>]*>[^<]*?)<object\\b[\\s\\S]*?</object>\\s*','gi');
		var rclean6 = new RegExp('\\b([\\w-]+)\\s*:\\s*([^;]+)','g');
		var rclean6a= new RegExp('\\burl\\(([^\\)]*)\\)','g');
		var rclean7 = new RegExp('^[^<]*','g');
		var rclean8 = new RegExp('(</?)(\\w+\\b|\\!)([^>]*>)([^<]*)','g');
		var rclean9 = new RegExp('^\s+');
		var rclean9a= new RegExp('(<script\\b[^>]*?>)([\\s\\S]+?)(</script>)','gi');
		var rclean9b= new RegExp('\\{Replace\\-Script\\-Code\\d+\\}','g');

		var rchars = [
			String.fromCharCode(162),
			String.fromCharCode(167),
			String.fromCharCode(169),
			String.fromCharCode(171),
			String.fromCharCode(174),
			String.fromCharCode(176),
			String.fromCharCode(187)
		];
		var rspecial= new RegExp('['+rchars.join("")+']', 'g');
		var rspecialfix = [
			"&cent;",
			"&sect;",
			"&copy;",
			"&#171;",
			"&reg;",
			"&deg;",
			"&raquo;"
		];

		// Clean the tag and attributes.
		html = html.replace( rclean1, '' )
			//.replace( rclean1a,' ')
			.replace( rclean1b, '' )
			.replace( rclean2, function(m){ return m.toLowerCase(); } )
			.replace( rclean3a, function(m) {
				return m.replace( rclean3b, function( m, m1, m2, m3, m4 ) {
					var val = ( m3 || m4 || '' ).replace( rclean4, '' );
					// Ensure any urls are correct.
					if ( $.cms && $.cms.page ) {
						switch (m1) {
							case "src":
							case "background":
							case "_overimg":
								val = $.cms.page.CleanUrl(val);
								break;
							case "href":
								val = $.cms.page.CleanUrl(val) || "/";
								break;
						}
					}
					return m1 + '="' + val + '"';
				});
			})
			.replace(rclean5,function(m,m1) {
				if ( $.cms.csseditor ) {
					var style = $.cms.csseditor.collapse(m1,true);
					return style ? ' style="' + style + '"' : "";
				}
				return m.replace( rclean6, function( m, m1, m2 ){
					if ( $.cms && $.cms.page ) {
						return m1.toLowerCase() + ':' + m2.replace( rclean6a, function(m3, m4) { return 'url(' + $.cms.page.CleanUrl(m4) + ')'; });
					} else {
						return m1.toLowerCase() + ':' + m2;
					}
				});
			})
			.replace(rclean5a, '' )
			.replace(rclean5c, '$1' )
			.replace(rclean5d, '$1' )
			.replace($.escapeRegex, function(m){ return $.escapeCodes[m]||$.escapeCodes2[m]||m; })
			.replace(rspecial,function(m){
				for ( var i=0; i<rchars.length; i++ ) {
					if ( m === rchars[i] ) {
						return rspecialfix[i];
					}
				}
				return m;
			});

		// If we've been asked to indent the HTML.
		if ( indent ) {
			// Build a script dictionary and extract all script contents.
			var script = {};
			var sindex = 0;
			html = html.replace(rclean9a, function( m, m1, m2, m3 ) {
				sindex++;
				var key = '{Replace-Script-Code'+sindex+'}';
				script[key] = m2;
				return m1 + key + m3;
			});

			// Initialize the parent-child rules.
			var nochild = function(tag,parent) {
				switch (tag) {
					case 'html':
						return true;
					case 'li':
						return parent.tag==='li';
					default:
						return false;
				}
			};

			// Initialize the self-closing rules.
			var selfclosing = function(item) {
				if (!item) return false;
				if (item.html.endsWith('/>')) {
					return true;
				} else {
					switch (item.tag) {
						case 'area':
						case 'base':
						case 'br':
						case 'embed':
						case 'hr':
						case 'img':
						case 'input':
						case 'link':
						case 'meta':
						case 'param':
							return true;
						default:
							return false;
					}
				}
			};

			// Initialize the array of page objects.
			var page = {};
			page.items = [];

			// If we have a literal token at the beginning of the HTML, add it directly.
			var m = rclean7.exec(html);
			if (m) {
				var start = m[0].replace(/^\s+/,'').replace(/\s+$/,' ');
				if (start) {
					page.items.push({text:start});
				}
			}

			// Iterate through the HTMLTags / literal content.
			var literal = null;
			var parent = page;
			while ( m = rclean8.exec(html) ) {
				// We will ignore the tbody tag completely.
				if (m[2]==='tbody') {
					continue;
				}

				// Is this a closing tag?
				var close = m[1]==='</' || (m[2]==='!' && m[3].endsWith('-->'));

				var pop = null;
				if (close) {
					// If we're closing the tag, walk up the tree.
					var p = parent;
					while ( p && p.tag !== m[2] ) {
						p = p.parent;
					}

					// If we found NO matching opening tag, we have some illegal HTML.  Add the closing tag directly.
					if (!p) {
						if (!parent.items) {
							parent.items = [];
						}
						parent.items.push({text:m[0]});
					} else {
						// Otherwise, add the closing tag.
						p.closing = m[1]+m[2]+m[3];
						if (m[4]) {
							p.closingText = m[4];
						}

						// Walk up the tree one level.
						parent = p.parent;
					}
				} else {
					// Create a new item.
					var item = {
						tag : m[2],
						html : m[1]+m[2]+m[3],
						text : m[4]
					};

					// Fix msie-bug of not always closing LI elements.
					if (item.tag==='li') {
						item.closing = '</li>';
					}

					// If this tag can't be a child of the current element, move up on level so it is added as a sibling.
					if ( parent.parent && nochild(item.tag,parent) ) {
						parent = item.parent = parent.parent;
					}

					// Note the relationship and add the item to the array.
					if (!parent.items) {
						parent.items = [];
					}
					if (parent.items.length) {
						item.prev = parent.items[parent.items.length-1];
					}
					item.parent = parent;
					parent.items.push(item);

					// Unless self-closed, the item is the new parent.
					if (selfclosing(item)) {
						item.self = true;
					} else {
						parent = item;
					}
				}
			}

			// Certain elements that contain only one child will not be indented in a nested fashion.
			var nesting = function(parent) {
				if ( !parent || !parent.items || !parent.items.length ) {
					return false;
				}

				switch (parent.tag) {
					case 'html':
					case 'head':
					case 'body':
						return true;
					case 'i':
					case 'b':
					case 'em':
					case 'u':
					case 'strong':
					case 'sub':
					case 'sup':
						return false;
					case 'td':
					case 'li':
					case 'a':
					case 'p':
					case 'span':
					case 'font':
					case 'if':
					case 'h1':
					case 'h2':
					case 'h3':
					case 'h4':
						return parent.items.length>1 || parent.items[0].tag==='div';
					default:
						return true;
				}
			};

			// Is this an inline element.
			var inline = function(parent) {
				switch (parent && parent.tag) {
					case 'i':
					case 'b':
					case 'em':
					case 'u':
					case 'strong':
					case 'a':
					case 'span':
					case 'font':
					case 'sub':
					case 'sup':
						return true;
					default:
						return false;
				}
			};

			// If we've turned off nesting, should we turn it back on at the end of certain tags?
			var renesting = function(parent) {
				if (!parent) {
					return false;
				}
				switch (parent.tag) {
					case 'td':
					case 'tr':
						return true;
					default:
						return false;
				}
			};

			// Clean a set of named styles.
			var cleanstyle = function(text,indent) {

				// If we have no content, return an empty string.
				text = $.trim(text);
				if (!text) return '';

				// Declare the css regex.
				var r_comments = /\/\*[\s\S]*?\*\//g;
				var r_style = /([\w:\.#,\*\-\\\s]+)\{([^\}]*)\}/g;
				var r_rgb = /rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i;
				var r_selector = /(^| |,)(\w+)(\.\w+)?/g;

				// Remove the commends and build the CSS object.
				var sheet = text.replace(r_comments,'');
				var css = [];
				var m;
				while( m = r_style.exec(sheet) ) {
					var s = $.trim(m[1]).replace(/\s+/,' ');
					css.push({s:s,v:$.trim(m[2])});
				}

				// Iterate through the css rules.
				var sb = ['\n'];
				for ( var i=0; i<css.length; i++ ) {
					// Add the selector.
					var name = $.trim(css[i].s.replace(r_selector,function(m,m1,m2,m3){return m1+m2.toLowerCase()+(m3||'');}));
					if (indent) {
						sb.push(indent);
					}
					if (i===0) {
						sb.push('\t');
					}
					sb.push(name);
					sb.push(' {\n');

					// Get the rules for the selector.
					var styles = css[i].v.split(';');
					for (var j=0;j<styles.length;j++) {
						var style = styles[j].split(':');
						if (style.length===2) {
							// If we have an RGB color definition, convert it to a hex color.
							if (r_rgb.test(style[1])) {
								style[1] = style[1].replace(r_rgb,function(m,m1,m2,m3){
									var c = {};
									c.r = parseInt(m1);
									c.g = parseInt(m2);
									c.b = parseInt(m3);
									var hex = 
										(c.r<16?'0':'')+c.r.toString(16)+
										(c.g<16?'0':'')+c.g.toString(16)+
										(c.b<16?'0':'')+c.b.toString(16);
									return '#'+hex;
								});
							}

							// Add the style rule.
							if (indent) sb.push(indent);
							sb.push('        ');
							sb.push($.trim(style[0].toLowerCase()));
							sb.push(': ');
							sb.push($.trim(style[1]));
							sb.push(';\n');
						}
					}

					// Close off the selector.
					if (indent) {
						sb.push(indent);
					}
					sb.push('\t');
					sb.push('}\n');
					if (indent) {
						sb.push(indent);
					}
				}

				return sb.join('');
			};

			// Clean multiple white space so it is just a single one.
			var clean = function(text) {
				return (''+text).replace(/\s+/g,' ');
			};

			// Trim the text after the opening tag.
			var trimcontent = function(text,parent,indent) {
				// If we don't have parent elements, or we have a PRE tag, return the text unchanged.
				if (!parent||!parent.parent||!text||parent.tag==='pre') {
					return text;
				}

				if (parent.tag==='style') {
					// Style content gets cleaned up.
					return cleanstyle(text,indent);
				} else if (parent.tag==='head' || parent.parent.tag==='head') {
					// Anything in the head gets fully trimmed.
					return $.trim(clean(text));
				} else {
					// Clean any leading spaces of the opening content inside a tag.
					return clean(text).replace(/^\s+/,'');
				}
			};

			// Trim the text after the closing tag.
			var trimend = function(text,parent) {
				// If we don't have parent elements, return the text unchanged.
				if (!parent||!parent.parent||!text) {
					return text;
				}

				// Anything in the head gets fully trimmed, as well as trs and tds.
				if ( parent.tag==='head' || parent.parent.tag==='head' || parent.tag==='td' || parent.tag==='tr' ) {
					return $.trim(clean(text));
				} else {
					if (parent.parent.items[parent.parent.items.length-1]===parent) {
						// If this is the last child tag of its parent, trim the white-space off the end.
						return clean(text).replace(/\s+$/,'');
					} else {
						// Otherwise clean the white space of the text.
						return clean(text);
					}
				}
			};

			// Recursive function to render the HTML.
			var render = function(parent,sb,level,nest) {
				if (parent) {
					// Make any special adjustments.
					switch (parent.tag) {
						case 'html':
							if (level>0) level -=1;
							break;
						case 'head':
						case 'body':
							level -=1;
							break;
					}
					
					// Get the size of the indent.
					var tabs = [];
					for ( var i=0; i<level; i++ ) {
						tabs.push('\t');
					}
					var indent = tabs.join('');

					// Add any opening html.
					if (parent.html) {
						// Add an indent if we're nesting this item.
						if (nest && sb[sb.length-1]==='\n') {
							sb.push(indent);
						}
						sb.push(parent.html);
					}

					if (parent.self) {
						// A self-closing item will get a line-feed after it unless we're not nesting.
						if (nest) sb.push('\n');
					} else {
						// If the current item shouldn't be nested, note that fact.
						if (!nesting(parent)) {
							nest = false;
						} else if (parent.tag) {
							if (parent.tag!=='p') {
								sb.push('\n');
							}
							nest = true;
						}
					}

					// Record if we're nesting the parent or not.
					parent.nest = nest;

					// If there is any literal text after the tag opening, add it.
					if (parent.text) {
						var text = trimcontent(parent.text,parent,indent);
						if (text) {
							if (nest && sb.length && sb[sb.length-1]==='\n') {
								sb.push(indent);
								if (parent.tag!=='br') sb.push('\t');
							}
							sb.push(text);
						}
					}
					
					// If we have child elements, recurse this method.
					if (parent.items && parent.items.length) {
						for ( var i=0; i<parent.items.length; i++ ) {
							var item = parent.items[i], next = parent.items[i+1];
							// If the first tag is a hard line break, add a line-feed.
							if ( nest && item.tag === 'br' && sb.length > 0 && sb[sb.length-1] !== '\n' ) {
								sb.push('\n');
							}

							// Render each of the child elements.
							render(item,sb,level+1,nest);

							// What is the last character in the string?
							var end = sb[sb.length-1];
							end = end && end[end.length-1];

							// Should we add a line feed?
							if ( item.closing && nest && end !== '\n' ) {
								if ( !next || end === ' ' || !inline(next) ) {
									sb.push('\n');
								}
							}

						}
					}

					// If there is any closing html, add it.
					if (parent.closing) {
						if (nest && sb[sb.length-1]==='\n') {
							sb.push(indent);
						}
						sb.push(parent.closing);
					}

					// If there is any closing text, add it.
					var text = null;
					if (parent.closingText) {
						text = trimend(parent.closingText,parent);
						if (text) {
							// If we have text coming after a p or a div.
							if ( (parent.tag === 'p' || parent.tag === 'div' ) && 
								 sb[sb.length-1] !== '\n' && 
								 !/^[\r\n]/.test(text) && 
								 $.trim(text).length ) {
								// Add line break (unless we already have one)
								sb.push('\n');
								if (indent) {
									sb.push(indent);
								}
								text = text.replace(/^\s+/,'');
							}
							sb.push(text);
						}
					}
				}
			};
			
			var sb = [];
			render(page,sb,-1,true);
			html = sb.join('').replace(rclean9b, function( m ) {
				return script[m];
			});
		}
		
		return html;
	};

	$.fn.cleanHtml = function(indent, absolute) {
		return $.cleanHtml(this.outerHtml(), indent, absolute);
	};

	// Double-space after sentences, prevent orphaned words at the end of paragraphs, and use em-dashes.
	$.printify = function(html) {
		if ( !html ) {
			return html;
		} else {
			return html.replace( />([^<]+)</g, function(m,m1){
				return '>'+
					m1.replace(/(\w\.) +(\w)/g, function(m,m1,m2){
						return m1+'&nbsp; '+m2;
					})+
					'<';
			}).replace( /(\w+) +(\S{1,20}<\/p>)/gi, function(m,m1,m2){
				return m1+'&nbsp;'+m2;
			}).replace( / - /g, ' &mdash; ');
		}
	};

	// Get the value of a .NET control.
	$.fn.formVal = function() {
		var val = null;
        var check = false;
		// If we have a table, get the sub values.
		if ( this.is('img') ) {
			return undefined;
		} else if ( this.is('table') ) {
			val = [];
			this.find(':input').each(function(i){
				var input = $(this);
				if ( input.is(':checked') ) {
					val.push( input.val() );
					check = true;
				} else if ( input.is(':text') ) {
					val.push( input.val() );
				}
			});
			if ( !val.length ) {
				val = null;
			} else if ( check ) {
				val = val.join(",");
			} else {
				val = val.join("");
			}
		} else if ( this.is(':checkbox') ) {
			val = this.is(':checked') ? true : "";
		} else if ( this.is(':radio') ) {
			val = this.is(':checked') ? this.val() : undefined;
		} else if ( this.is('textarea') ) {
			var editor = this.parent('div').data('editor');
			if ( editor ) {
				return editor.getData();
			} else {
				return this.val();
			}
		} else {
			val = this.val();
		}
		return val;
	};

	// Get a dictionary of query variables.
	$.getQuery = function() {
		// Grab the querystring part of the url.
		var query = {};
		var items = window.location.href.split('?').pop().split('&');
		
		// Iterate through each of the elements.
		for (var i=0;i<items.length;i++) {
			// Assign anything that has a proper name/value pair.
			var pair = items[i].split('=');
			if (pair.length==2) {
				query[pair[0].toLowerCase()] = pair[1];
			}
		}
		
		return query;
	};

	// Get a querystring value.
	$.getQueryString = function(field) {
		// Return the querystring.
		if ( field ) {
			field = field.toLowerCase();
		}
		return $.getQuery()[field];
	};

	// Rapid validation of a click event to see if we clicked on a link or a span/img inside a link.,
	$.getLinkTarget = function(e) {
		// Get the target and see if we clicked on a link.
		if ( !e || !e.target || !e.target.parentNode ) {
			return null;
		}
		if ( (e.target.nodeName||"").toLowerCase() === 'a' ) {
			return $(e.target);
		} else if ( (e.target.parentNode.nodeName||"").toLowerCase() === 'a' ) {
			return $(e.target.parentNode);
		} else if ( (e.target.parentNode.parentNode.nodeName||"").toLowerCase() === 'a' ) {
			return $(e.target.parentNode.parentNode);
		} else {
			return null;
		}
	};

	// Get any action off of a javascript link.
	$.getLinkAction = function(e) {
		var link = $.getLinkTarget(e),
			href = link && link.attr('href'),
			m = href && /^javascript:(\w+)\('([^']+)'/i.exec(href),
			fn = m && m[1],
			action = m && m[2];

		return {
			link: link,
			href: href,
			fn: fn,
			action: action
		};
	};

	// Add an item to an array, if it doesn't exist already.
	Array.prototype.addItem = function(item) {
		if ( item === undefined ) {
			return;
		}
		for (var i=0;i<this.length;i++) {
			var el = this[i];
			if (el == item) {
				return this;
			}
		}
		this.push(item);
		return this;
	};

	// Remove an item from an array, if it exists.
	Array.prototype.removeItem = function(item) {
		if ( item === undefined ) {
			return;
		}
		for (var i=0;i<this.length;i++) {
			var el = this[i];
			if (el == item) {
				if (i==0) {
					this.shift();
				} else if ( i == this.length-1 ) {
					this.pop();
				} else {
					this.splice(i,1);
				}
				return this;
			}
		}
		return this;
	};

	// What is the index of this item in the array?
	Array.prototype.indexOf = function(item) {
		if ( item === undefined ) {
			return;
		}
		for (var i=0;i<this.length;i++) {
			var el = this[i];
			if (el == item) {
				return i;
			}
		}
		return -1;
	};

	// Sun the contents of an array.
	Array.prototype.sum = function() {
		for (var i = 0, L = this.length, sum = 0; i < L; sum += this[i++] );
		return sum;
	};

	// Specialized array sort functions.
	$.sort = {
		// Swap two positions of an array.
		swap: function(array,a,b) {
			var tmp = array[a];
			array[a] = array[b];
			array[b] = tmp;
		},

		// Separate part of an array into high/low.
		partition: function(array, begin, end, pivot, fn) {
			// Get the pivit point.
			var piv = array[pivot];
			$.sort.swap(array, pivot, end-1);

			// Move all items in front of or after the pivot point.
			var store = begin;
			var ix;
			for ( ix = begin; ix<end-1; ++ix ) {
				if ( fn ? fn(array[ix],piv) <= 0 : array[ix] <= piv ) {
					$.sort.swap(array, store, ix);
					++store;
				}
			}
			$.sort.swap(array,end-1, store);

			return store;
		},

		// Perform a quicksort on a part of an array.
		quick: function(array, begin, end, fn) {
			if ( end-1 > begin ) {
				// Get a pivot point.
				var pivot = begin + Math.floor( Math.random() * (end-begin) );

				// Partition the array in to pre-sorted halves.
				pivot = $.sort.partition( array, begin, end, pivot, fn );

				// Recursivly sort each half.
				$.sort.quick( array, begin, pivot, fn );
				$.sort.quick( array, pivot+1, end, fn );
			}
		}
	};

	// Quick sort an array with a b-tree type algorithm.
	Array.prototype.quickSort = function(fn){
		$.sort.quick( this, 0, this.length, fn );
	};

	// Case-insensitive string comparison match.
	String.prototype.startsWith = function(data) {
		if (!data) {
			return false;
		}
		var data2 = (''+data).toLowerCase();
		return this.toLowerCase().indexOf(data2) == 0;
	};

	// Case-insensitive string comparison match.
	String.prototype.endsWith = function(data) {
		if (!data) {
			return false;
		}
		var data2 = (''+data).toLowerCase();
		var pos = this.toLowerCase().indexOf(data2);
		return pos >= 0 && pos == this.length-data2.length;
	};

	// Case-insensitive string comparison match.
	String.prototype.contains = function(data) {
		if (!data) {
			return false;
		}
		var data2 = (''+data).toLowerCase();
		return this.toLowerCase().indexOf(data2) >= 0;
	};

	// Get the standard timezone offset.
	Date.prototype.stdTimezoneOffset = function() {
		var jan = new Date(this.getFullYear(), 0, 1);
		var jul = new Date(this.getFullYear(), 6, 1);
		return Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset());
	};

})(jQuery);

