$(document).ready(function()
{
	//Modify the standard .NET client validation so we can have a bit more control over presentation
	ModifyASPNETValidation();

	//Handle all links with rel = external
	$('a:not(.PreviewInfoLink)').ExternalLinks();

	//Force all links with rel 'top' to open in the top window
	$('a[rel="top"]').TopLinks();

	//Adds overlay behaviour to the 2 social links in the main nav
	InitOverlays();

	//Fix ie6 issues
	FixIE6();
});

function InitOverlays()
{
	$('div#nav li.social li.shareWithFriend a').click(function()
	{
		var overlayUrl = GetOverlayUrl($(this).attr('href'));
		var hash = window.location.hash;
		if (hash.length > 0)
		{
			overlayUrl += '&h=' + hash.replace('#', '');
		}
		$.colorbox({ width: '600px', height: '500px', iframe: true, href: overlayUrl });
		return false;
	});

	var emailUpdatesLink = $('div#nav li.social li.emailUpdates a');
	if (emailUpdatesLink.length > 0)
	{
		var overlayUrl = GetOverlayUrl(emailUpdatesLink.attr('href'));
		emailUpdatesLink.colorbox({ width: '600px', height: '400px', iframe: true, href: overlayUrl });
	}
}

function GetOverlayUrl(baseUrl)
{
	var path = jQuery.url.setUrl(baseUrl).attr('path');
	var overlayPath = path + '-overlay';
	return baseUrl.replace(path, overlayPath);
}

function trackGAPageview(pageName)
{
	if (typeof (_gaq) != 'undefined')
	{
		_gaq.push(['_trackPageview', pageName]);
	}
}

function trackGAEvent(category, action, label, value)
{
	if (typeof (_gaq) != 'undefined')
	{
		_gaq.push(['_trackEvent', category, action, label, value]);
	}
}

function FixIE6()
{
	if ($('body').hasClass('IE6'))
	{
		//Add a hover class to the top level menu items so we can tell when we need to drop down the sub navs
		$('div#nav li.level1').hover(function()
		{
			var item = $(this);
			item.addClass('hover');
			if (item.hasClass('last'))
			{
				item.addClass('lastHover');
			}
		},
        function()
        {
        	var item = $(this);
        	item.removeClass('hover');
        	if (item.hasClass('last'))
        	{
        		item.removeClass('lastHover');
        	}
        });
	}
}

//Re-assigns a couple of the ASP.NET validation JS functions to provide a more flexible approach
function ModifyASPNETValidation()
{
	//Hi-jack the ASP.NET error display only if required
	if (typeof (Page_ClientValidate) != "undefined")
	{
		ValidatorUpdateDisplay = NicerValidatorUpdateDisplay;
		AspPage_ClientValidate = Page_ClientValidate;
		Page_ClientValidate = NicerPage_ClientValidate;
	}

	//Add validation statuses to any validation controls that may be visible (i.e. visible when the page loads)
	$('span.validation:visible,span.EditingFormErrorLabel:visible').each(function()
	{
		AddValidationStatus($(this));
	});
}

function AddValidationStatus(obj)
{
	if (obj.hasClass('validation') || obj.hasClass('EditingFormErrorLabel'))
	{
		//We'll look for a validation container to set the status on
		var vc = obj.parents('div.validationContainer');
		if (vc.length == 0)
		{
			//Fall back to div.formInput
			vc = obj.parents('div.formInput');
		}
		vc.addClass('invalidInput');
	}
}

function RemoveValidationStatus(obj)
{
	if (obj.hasClass('validation') || obj.hasClass('EditingFormErrorLabel'))
	{
		//We'll look for the validation container to remove the status
		var vc = obj.parents('div.validationContainer');
		if (vc.length == 0)
		{
			//Fall back to div.formInput
			vc = obj.parents('div.formInput');
		}
		vc.removeClass('invalidInput');
	}
}

//Extends the classic ASP.NET validation
function NicerValidatorUpdateDisplay(val)
{
	var $val = $(val);
	if (val.isvalid)
	{
		//Hide the validation control
		$val.hide();

		//Remove the validation status if there are no more validaiton controls visible
		if ($val.parent().find('span.validation:visible,span.EditingFormErrorLabel:visible').length == 0)
		{
			RemoveValidationStatus($val);
		}
	}
	else
	{
		//Show the validation control
		$val.show();

		//Add the validation status
		AddValidationStatus($val);
	}
}

//Extends classic ASP.NET validation to include parent element styling
function NicerPage_ClientValidate(validationGroup)
{
	var valid = AspPage_ClientValidate(validationGroup);

	// remove the invalid states
	$('.invalidInput').removeClass('invalidInput');
	
	// if the page is valid, do nothing
	if (valid)
	{
		return valid;
	}
	
	// loop through all validators
	for (var index in Page_Validators)
	{
		// if the current validator is not valid, add class to parent
		if (!Page_Validators[index].isvalid)
			$('#' + Page_Validators[index].controltovalidate).closest('.formInput').addClass('invalidInput');
	}

	return valid;
}

//ExternalLinks - tracking based on http://www.iqcontent.com/blog/2007/02/tracking-documents-and-external-links-in-google-analytics/
(function($)
{
	$.fn.extend({
		ExternalLinks: function()
		{
			return this.each(function()
			{
				if (this.tagName != 'A')
				{
					return false;
				}

				var link = String(this);
				var linkHost = this.hostname;
				var siteHost = location.host;

				if (link.match(/^mailto:/i))
				{
					$(this).removeAttr('rel');
					$(this).click(function()
					{
						return HandleMailToLink(this);
					});
				}
				else if (link.match(/^skype:/i))
				{
					$(this).removeAttr('rel');
					$(this).click(function()
					{
						return HandleSkypeCallLink(this);
					});
				}
				else if (linkHost == siteHost || (linkHost.substr(0, 6) == 'secure' && linkHost.substr(7) == siteHost.substr(4)))
				{
					var parts = link.split('?');
					var path = parts[0];
					if (path.match(/\.(doc|pdf|xls|ppt|zip|txt|vsd|vxd|js|css|rar|exe|wma|mov|avi|wmv|mp3|ashx)$/))
					{
						ModifyTitleAttribute(this);
						$(this).click(function()
						{
							return HandleDocumentLink(this);
						});
						return;
					}
				}
				else
				{
					ModifyTitleAttribute(this);
					$(this).click(function()
					{
						return HandleExternalLink(this);
					});
				}
			});

			function HandleMailToLink(anchor)
			{
				var email = anchor.href.substring(7);
				TrackLink('email', email);

				return true;
			}

			function HandleSkypeCallLink(anchor)
			{
				var username = anchor.href.substring(6);
				TrackLink('skype', username);

				return true;
			}

			function HandleDocumentLink(anchor)
			{
				var doc = CleanURL(anchor.pathname, false);
				TrackLink('documents', doc);

				window.open(anchor.href);
				return false;
			}

			function HandleExternalLink(anchor)
			{
				var link = CleanURL(anchor.hostname + '/' + anchor.pathname, true);
				TrackLink('external', link);

				window.open(anchor.href);
				return false;
			}

			function TrackLink(type, virtualPath)
			{
				var linkStr = CleanURL('/' + type + '/' + virtualPath, true);
				trackGAPageview(linkStr);
			}

			function CleanURL(url, end)
			{
				var url = url.toString();
				var urlLen = url.length;

				if (end)
				{
					if (url.charAt((urlLen - 1)) == '/')
					{
						url = url.substring(0, (urlLen - 1));
					}
				}
				else
				{
					if (url.charAt(0) == '/')
					{
						url = url.substring(1, urlLen);
					}
				}
				return url;
			}

			function ModifyTitleAttribute(anchor)
			{
				var jAnchor = $(anchor);
				var title = $(anchor).attr('title');
				if (title.length > 0)
				{
					title += ' [opens in a new window]';
				}
				else
				{
					title += 'Opens in a new window';
				}
				jAnchor.attr('title', title);
			}
		}
	});
})(jQuery);

//TopLinks forces a link to open in the top window
(function($)
{
	$.fn.extend({
		TopLinks: function()
		{
			return this.each(function()
			{
				if (this.tagName != 'A')
				{
					return false;
				}

				$(this).click(function()
				{
					top.location = this.href;
					return false;
				});
			});
		}
	});
})(jQuery);

// JQuery URL Parser
// Written by Mark Perkins, mark@allmarkedup.com
// License: http://unlicense.org/ (i.e. do what you want with it!)
jQuery.url = function()
{
	var segments = {};
	var parsed = {};

	/**
	* Options object. Only the URI and strictMode values can be changed via the setters below.
	*/
	var options = {
		url: window.location, // default URI is the page in which the script is running		
		strictMode: false, // 'loose' parsing by default	
		key: ["source", "protocol", "authority", "userInfo", "user", "password", "host", "port", "relative", "path", "directory", "file", "query", "anchor"], // keys available to query 
		q: {
			name: "queryKey",
			parser: /(?:^|&)([^&=]*)=?([^&]*)/g
		},
		parser: {
			strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,  //less intuitive, more accurate to the specs
			loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/ // more intuitive, fails on relative paths and deviates from specs
		}
	};

	/**
	* Deals with the parsing of the URI according to the regex above.
	* Written by Steven Levithan - see credits at top.
	*/
	var parseUri = function()
	{
		str = decodeURI(options.url);

		var m = options.parser[options.strictMode ? "strict" : "loose"].exec(str);
		var uri = {};
		var i = 14;

		while (i--)
		{
			uri[options.key[i]] = m[i] || "";
		}

		uri[options.q.name] = {};
		uri[options.key[12]].replace(options.q.parser, function($0, $1, $2)
		{
			if ($1)
			{
				uri[options.q.name][$1] = $2;
			}
		});

		return uri;
	};

	/**
	* Returns the value of the passed in key from the parsed URI.
	* 
	* @param string key The key whose value is required
	*/
	var key = function(key)
	{
		if (jQuery.isEmptyObject(parsed))
		{
			setUp(); // if the URI has not been parsed yet then do this first...	
		}
		if (key == "base")
		{
			if (parsed.port !== null && parsed.port !== "")
			{
				return parsed.protocol + "://" + parsed.host + ":" + parsed.port + "/";
			}
			else
			{
				return parsed.protocol + "://" + parsed.host + "/";
			}
		}
		return (parsed[key] === "") ? null : parsed[key];
	};

	/**
	* Returns the value of the required query string parameter.
	* 
	* @param string item The parameter whose value is required
	*/
	var param = function(item)
	{
		if (jQuery.isEmptyObject(parsed))
		{
			setUp(); // if the URI has not been parsed yet then do this first...	
		}
		return (parsed.queryKey[item] === null) ? null : parsed.queryKey[item];
	};

	/**
	* 'Constructor' (not really!) function.
	*  Called whenever the URI changes to kick off re-parsing of the URI and splitting it up into segments. 
	*/
	var setUp = function()
	{
		parsed = parseUri();
		getSegments();
	};

	/**
	* Splits up the body of the URI into segments (i.e. sections delimited by '/')
	*/
	var getSegments = function()
	{
		var p = parsed.path;
		segments = []; // clear out segments array
		segments = parsed.path.length == 1 ? {} : (p.charAt(p.length - 1) == "/" ? p.substring(1, p.length - 1) : path = p.substring(1)).split("/");
	};

	return {
		/**
		* Sets the parsing mode - either strict or loose. Set to loose by default.
		*
		* @param string mode The mode to set the parser to. Anything apart from a value of 'strict' will set it to loose!
		*/
		setMode: function(mode)
		{
			options.strictMode = mode == "strict" ? true : false;
			return this;
		},
		/**
		* Sets URI to parse if you don't want to to parse the current page's URI.
		* Calling the function with no value for newUri resets it to the current page's URI.
		*
		* @param string newUri The URI to parse.
		*/
		setUrl: function(newUri)
		{
			options.url = newUri === undefined ? window.location : newUri;
			setUp();
			return this;
		},
		/**
		* Returns the value of the specified URI segment. Segments are numbered from 1 to the number of segments.
		* For example the URI http://test.com/about/company/ segment(1) would return 'about'.
		*
		* If no integer is passed into the function it returns the number of segments in the URI.
		*
		* @param int pos The position of the segment to return. Can be empty.
		*/
		segment: function(pos)
		{
			if (jQuery.isEmptyObject(parsed))
			{
				setUp(); // if the URI has not been parsed yet then do this first...	
			}
			if (pos === undefined)
			{
				return segments.length;
			}
			return (segments[pos] === "" || segments[pos] === undefined) ? null : segments[pos];
		},
		attr: key, // provides public access to private 'key' function - see above		
		param: param // provides public access to private 'param' function - see above		
	};
} ();

/**
* hoverIntent r5 // 2007.03.27 // jQuery 1.1.2+
* <http://cherne.net/brian/resources/jquery.hoverIntent.html>
* 
* @param  f  onMouseOver function || An object with configuration options
* @param  g  onMouseOut function  || Nothing (use configuration options object)
* @author    Brian Cherne <brian@cherne.net>
*/
(function($) { $.fn.hoverIntent = function(f, g) { var cfg = { sensitivity: 7, interval: 100, timeout: 0 }; cfg = $.extend(cfg, g ? { over: f, out: g} : f); var cX, cY, pX, pY; var track = function(ev) { cX = ev.pageX; cY = ev.pageY; }; var compare = function(ev, ob) { ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); if ((Math.abs(pX - cX) + Math.abs(pY - cY)) < cfg.sensitivity) { $(ob).unbind("mousemove", track); ob.hoverIntent_s = 1; return cfg.over.apply(ob, [ev]); } else { pX = cX; pY = cY; ob.hoverIntent_t = setTimeout(function() { compare(ev, ob); }, cfg.interval); } }; var delay = function(ev, ob) { ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); ob.hoverIntent_s = 0; return cfg.out.apply(ob, [ev]); }; var handleHover = function(e) { var p = (e.type == "mouseover" ? e.fromElement : e.toElement) || e.relatedTarget; while (p && p != this) { try { p = p.parentNode; } catch (e) { p = this; } } if (p == this) { return false; } var ev = jQuery.extend({}, e); var ob = this; if (ob.hoverIntent_t) { ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); } if (e.type == "mouseover") { pX = ev.pageX; pY = ev.pageY; $(ob).bind("mousemove", track); if (ob.hoverIntent_s != 1) { ob.hoverIntent_t = setTimeout(function() { compare(ev, ob); }, cfg.interval); } } else { $(ob).unbind("mousemove", track); if (ob.hoverIntent_s == 1) { ob.hoverIntent_t = setTimeout(function() { delay(ev, ob); }, cfg.timeout); } } }; return this.mouseover(handleHover).mouseout(handleHover); }; })(jQuery);

