//Ensure that jQuery won't interfere with other JavaScript libraries, particularily those being used in 3rd party applications.
jQuery.noConflict();

// Spint Namespace
var Sprint = {

	content: Sprint20Content, //Pull the values from the sprint.content JS file and add them to the Sprint namespace
	currentLanguage: Sprint20Language,
	
	nav: {},
	modal: {
		elem: {},
		dimensions: {}
	},
	
	tooltip: {
		elem: {}
	},
	
	shade: {},
	
	ieShims: {
		currentShims: 0,
		elem: new Array()
	},
	
	formFieldTypes: {
		/*
			Username
			- Must start with a letter: [a-zA-Z]{1}
			- Must be followed by followed by 5-32 of these characters: a-z, A-Z, 0-9, _, -, .: [a-zA-Z0-9_\.\-]{5,32}
				- ensures that usernames will be 6-33 chars
		*/
		username: /^[a-zA-Z]{1}[a-zA-Z0-9_\.\-]{5,32}$/,
		
		/*
			Password
			- Any 6-33 characters: .{6,33}
		*/
		password: /^.{6,33}$/,
		
		/*
			Email Address
			- Starts with 1 or more of these charcters a-z, A-Z, 0-9, _, -, .: [a-zA-Z0-9_\.\-]+
			- Followed by an @ sign: \@
			- Followed by 1 or more groups of the following characters ending in a period: ([a-zA-Z0-9\-]+\.)+
				- 1 or more of these characters a-z, A-Z, 0-9, -: [a-zA-Z0-9\-]+
				- ending in a period: \.
			- Followed by 2-4 of the following characters a-z, A-Z, 0-9: [a-zA-Z0-9]{2,4}
		*/
		emailAddress: /^[a-zA-Z0-9_\.\-]+\@([a-zA-Z0-9\-]+\.)+[a-zA-Z0-9]{2,4}$/,
		
		/*
			Phone Number
			- Optional bracket: \(?
			- 3 digits for area code: \d{3}
			- Optional bracket: \)?
			- Optional Space or Dash [\s|\-]?
			- 3 digits: \d{3}
			- Optional Space or Dash [\s|\-]?
			- 4 digits: \d{4}
		*/
		phoneNumber: /^\(?\d{3}\)?[\s|\-]?\d{3}[\s|\-]?\d{4}$/,
		
		/*
			Check Box
			- returns true if it's checked, false if not
			- expects the field to be passed in as a jQuery object
		*/
		checkbox: function(field) {
			if (field.is(":checked")) {
				return true;
			}
			return false;
		},
		
		/*
			Radio Button
			- returns true if one option is checked, false if not
			- expects the group of fields to be passed in as a jQuery object
		*/
		radioButton: function(field) {
			if (field.is(":checked")) {
				return true;
			}
			return false;
		},
		
		/*
			ESN
			- 11 digits: \d{11} OR
			- 8 Hex Digits [A-F0-9]{8}
		*/		
		ESN: /^\d{11}$|^[A-F0-9]{8}$/,
		
		/*
			MEID
			- 18 digits: \d{18} OR
			- 14 Hex Digits [A-F0-9]{14}
		*/		
		MEID: /^\d{18}$|^[A-F0-9]{14}$/,
		
		/*
			BAN
			- 9 digits: \d{9}
		*/		
		BAN: /^\d{9}$/,
		
		/*
			PIN (personal identification number)
			- 6-10 digits: \d{6,10}
		*/
		PIN: /^\d{6,10}$/,
		
		/*
			SIM
			- 15 digits: \d{15}
		*/
		SIM: /^\d{15}$/,
		
		/*
			Validation Code
			- 8 digits: \d{8}
		*/
		validationCode: /^\d{8}$/
	},
	
	//Helper functions
	fn: {
		/*
		* validateForm
		*
		* function to determine the width of the browser's scrollbars
		*
		* @param form: the form to be validated (jQuery object)
		* @param formFields: object containing all of the form fields and their field types
		*
		* @returns boolean or object: true if form is valid, formErrors object if it's not
		*/
		validateForm: function(form, formFields) {
			var $ = jQuery; //Refer to jQuery as $ in this function
			
			var formValid = true;
			
			var formErrors = {				
			};
			
			function addErrorMessage(fieldName, errorMessage) {
				formErrors[fieldName] = {
					name: fieldName,
					errorMessage: errorMessage
				};
			}
			
			//run through each form field and determine if the fields are valid
			for (field in formFields) {
				var currentField = formFields[field];
				var currentFieldObj = form.find("[name='"+currentField.name+"']");
				var currentFieldValue = currentFieldObj.val();

				//First, check to see if it's a required field that's not filled in.
				if (currentField.required && $.trim(currentFieldValue) == "") {
					//required but not been filled in...
					formValid = false;
					
					//Add the error to the formErrors object
					addErrorMessage(currentField.name, currentField.emptyErrorMessage);
				}
				else if ($.trim(currentFieldValue) != "") {
					//filled in... check to see if the value is valid
					
					if (currentField.customValidationRule) {
						//If the field has a cutom validation rule, use this rule instead of the default for this field type
						if (!currentField.customValidationRule.test && !currentField.customValidationRule(currentFieldObj)) {
							formValid = false;
							
							//Add the error to the formErrors object
							addErrorMessage(currentField.name, currentField.invalidErrorMessage);
						}
						else if (currentField.customValidationRule.test && !currentField.customValidationRule.test(currentFieldValue)) {
							formValid = false;
							
							//Add the error to the formErrors object
							addErrorMessage(currentField.name, currentField.invalidErrorMessage);
						}
					}
					else {
						//Use the default rules for this field type
						
						//Check to see if this field has multiple valid field types
						var currentFieldTypes = currentField.type.split(",");
						
						if (currentFieldTypes.length > 1) {
							
							//This field has multiple valid types, check all of them. Start by assuming they'll all fail, and break on the first one that passes one of the field type tests.
							var oneTestPassed = false;
							
							var numberOfFieldTypes = currentFieldTypes.length;
							
							for (i = 0; i < numberOfFieldTypes; i++) {
								
								if (!Sprint.formFieldTypes[currentFieldTypes[i]].test && Sprint.formFieldTypes[currentFieldTypes[i]](currentFieldObj)) {
									//Test passed
									oneTestPassed = true;
								}
								else if (Sprint.formFieldTypes[currentFieldTypes[i]].test && Sprint.formFieldTypes[currentFieldTypes[i]].test(currentFieldValue)) {
									//Test passed
									oneTestPassed = true;
									break;
								}
							}
							
							//If none of the tests were passed, add the error to the formErrors object
							if (!oneTestPassed) {
								formValid = false;
								
								addErrorMessage(currentField.name, currentField.invalidErrorMessage);
							}
						}
						else if (currentField.type != "match") {
							//If this field isn't supposed to match another field, check that first (convert both strings to lowercase)
							if (currentField.mustNotMatch && currentFieldValue.toLowerCase() == form.find("[name='"+currentField.mustNotMatch+"']").val().toLowerCase()) {
								formValid = false;
			
								//Test failed, add the error to the formErrors object
								addErrorMessage(currentField.name, currentField.invalidErrorMessage);
							}
							else {
								
								if (!Sprint.formFieldTypes[currentField.type].test && !Sprint.formFieldTypes[currentField.type](currentFieldObj)) {
									formValid = false;
				
									//Test failed, add the error to the formErrors object
									addErrorMessage(currentField.name, currentField.invalidErrorMessage);
								}
								else if (Sprint.formFieldTypes[currentField.type].test && !Sprint.formFieldTypes[currentField.type].test(currentFieldValue)) {
									formValid = false;
				
									//Test failed, add the error to the formErrors object
									addErrorMessage(currentField.name, currentField.invalidErrorMessage);
								}

							}
						}
						else if (currentField.type == "match" && currentFieldValue != form.find("[name='"+currentField.mustMatch+"']").val()) {
							//This field needs to match another field but it doesn't, add the error to the formErrors object
							formValid = false;

							addErrorMessage(currentField.name, currentField.invalidErrorMessage);
						}
					}
				}
			}
			
			return (formValid) ? formValid : formErrors;
		},
		
		/*
		* getScrollbarWidth
		*
		* function to determine the width of the browser's scrollbars
		*
		* @returns int: Sprint.scrollbarWidth
		*/
		getScrollbarWidth: function() {
			var $ = jQuery; //Refer to jQuery as $ in this function

			// if the Sprint namespace doesn't currently have this value stored, determine the width and store it...
			if (!Sprint.scrollbarWidth) {
			
				var outerDiv = $("<div />");
				outerDiv.css({
					position:  "absolute",
					left:      "-1000px",
					top:       "-1000px",
					width:     "50px",
					height:    "50px",
					overflow:  "hidden"
				});
				
				// create another div and append it to outerDiv
				$("<div />").css("height", "100px").appendTo(outerDiv);
				
				// append outerDiv to the body
				outerDiv.appendTo("body");
				
				// find the internal div within outerDiv and store its width (by default, its width will be 100% of the outerDiv width).
				var w1 = $("div", outerDiv).width();
				
				// force outerDiv to have a vertical scrollbar (this will shrink the inner div's width by the exact width of the scrollbar)
				outerDiv.css("overflow-y", "scroll");
				
				// store the new width of the inner div.
				var w2 = $("div", outerDiv).width();
				
				// get rid of the outerDiv, as it's no longer needed
				outerDiv.remove();

				Sprint.scrollbarWidth = (w1 - w2);
			}
			
			return Sprint.scrollbarWidth;
		}
	}
	
};

//Wrap everything in an anonymous function and pass in the jQuery object where it will internally be referred to as $. This will allow typical jQuery syntax: $()
(function($) {
	
	//Extend jQuery's function set with Sprint specific functions
	$.fn.extend({
		
		/*
		* openModal
		*
		* function to open a modal window
		*
		* @param scriptOptions: Object, defines options that will override the defaults
		*
		*/
		openModal: function(scriptOptions) {
		
			// if there's already a modal open, it must stay open, so move on without opening a new one
			if (Sprint.modal.elem && Sprint.modal.elem.length > 0) {
				return this;
			}
			
			var defaults = {
				width:              750,     // integer: default modal width (in pixels)
				isCommented:        false,   // boolean: set this value to true if the modal content is commented out
				ajaxContent:        false,   // boolean: set this value to true if the content will be loaded via ajax
				ajaxPath:           "",      // string: the path for the content being loaded via ajax
				overlayColor:       "#000",  // string (hex): the color of the overlay
				overlayOpacity:     0.4,     // integer: opacity level of the overlay
				allowOverlayClick:  true,    // boolean: set this value to false to prevent clicks on the overlay from closing the modal.
				delay:              0,       // integer (milliseconds): the amount of delay before showing the modal
				closeButton:        true,    // boolean: set this value to false to not display the close button
				openCallback:       null,    // function: this callback will be run after the modal opens
				closeCallback:      null     // function: this callback will be run when the modal closes
			};

			var options = $.extend(defaults, scriptOptions);

			// Store the modal options
			Sprint.modal.options = options;

			// Populate the dimensions object with the current document/window dimmensions
			Sprint.modal.dimensions.documentWidth	= docWidth	= $(document).width();
			Sprint.modal.dimensions.documentHeight  = docHeight = $(document).height();
			Sprint.modal.dimensions.windowWidth	    = winWidth	= $(window).width();
			Sprint.modal.dimensions.windowHeight	= winHeight = $(window).height();
			Sprint.modal.dimensions.bodyWidth	    = bodyWidth	= $("body").width();
			Sprint.modal.dimensions.bodyHeight      = bodyHeight = $("body").height();
			Sprint.modal.dimensions.overlayWidth	= ($.browser.msie && parseInt($.browser.version) < 7) ? bodyWidth : docWidth; //IE6 uses bodyWidth, everything else uses docWidth
			Sprint.modal.dimensions.overlayHeight	= (docHeight < winHeight) ? winHeight : docHeight;
			Sprint.modal.dimensions.scrollLeft		= $(document).scrollLeft();
			Sprint.modal.dimensions.scrollTop		= $(document).scrollTop();
			
			var dimensions = Sprint.modal.dimensions;

			// store the content of the current modal
			Sprint.modal.content = this;
			
			var thisModal = $(this);
			
			// store the original content that was in the modal before opening. This will ensure that if any processing takes place on the content after the modal opens (using it's openCallback function), it will revert to it's pre-processed state when the modal closes. (Which will also ensure that when the modal opens again, the processing takes place the exact same way, every time).
			Sprint.modal.originalContent = $(this).clone(true);

			// showModal
			showModal = function() {
				if (Sprint.modal.timer) {
					clearTimeout(Sprint.modal.timer);
				}

				// apply a visual overlay
				var modalOverlay = $("<div id=\"modalOverlay\"></div>").css({
					backgroundColor:  options.overlayColor,
					opacity:		  options.overlayOpacity,
					width:		      dimensions.overlayWidth,
					height:		      dimensions.overlayHeight
				}).appendTo("body").addShim(); // addShim(): use iframe to block out form elements (windowed elements) in IE

				// Create the markup for the Modal
				var modalMarkup = modalCloseButton = "";
				
				// Do we need a close button?
				if (options.closeButton) {
					modalCloseButton = "<a href=\"#\" class=\"modalChromeCloseButton closeModal\"><img src=\"/_images/_template/modal/close.png\" width=\"21\" height=\"19\" alt=\""+Sprint.content.modal.closeButtonAltText[Sprint.currentLanguage]+"\"></a>";
				}
				
				//Top Chrome
				modalMarkup += "<div class=\"modalChromeTopLeft\"><div class=\"modalChromeTopRight\">" + modalCloseButton + "</div></div>";
				
				//Middle Chrome
				modalMarkup += "<div class=\"modalChromeLeft\"><div class=\"modalChromeRight\"><div class=\"modalContent content\"></div></div></div>";
				
				//Bottom Chrome
				modalMarkup += "<div class=\"modalChromeBottomLeft\"><div class=\"modalChromeBottomRight\"></div></div>";
				
				// Store the modal in the Sprint.modal.elem object
				Sprint.modal.elem = $("<div id=\"modalHolder\" class=\"sprint\" />").append(modalMarkup).appendTo("body");

				// if the markup for the modal is commented out in the HTML doc, remove the comments
				if (options.isCommented) {
					Sprint.modal.content.html(Sprint.modal.content.html().replace(/^\s*<!--\s*|\s*-->\s*$/mg, ""));
				}

				//Move the modal to a modal holder (append _holder to the curren modal parent's ID)
				Sprint.modal.content.parent().attr("id", Sprint.modal.content.attr("id")+"_holder");
				Sprint.modal.content.appendTo(".modalContent");

				// Set the modal's size, and then hide it
				Sprint.modal.elem.sizeModal(options).hide();
				
				//Create a function that will fire after the modal window opens that handles some further event binding.
				var afterOpen = function() {
					
					//Remove focus from anything that might currently have it
					$("*").blur();
				
					//If there's ajax content to be loaded, or an openCallback function...
					if (options.openCallback || options.ajaxContent) {
						setTimeout(function() {
						
							if (options.ajaxContent) {
								Sprint.modal.elem.loadModalContent(options.ajaxPath, thisModal);
							}
							
							if (options.openCallback) {
								options.openCallback();
							}
							
						}, 1); //Set a very quick timeout so that this definitely fires AFTER the modal opens.
					}
					
					// bind the ESC key to close the modal
					$(document).bind("keydown", function(key) {
						if (key.keyCode == 27) {
							if (Sprint.modal.elem) {
								Sprint.modal.elem.closeModal();
							}							
							if (options.closeCallback) {
								options.closeCallback();
							}
							return false;
						}
					});
					
					if (options.allowOverlayClick) {
						//clicking on the overlay will also close the modal
						$("#modalOverlay").bind("click", function() {
							
							//Close the modal
							Sprint.modal.elem.closeModal();
	
							//Fire off the closeCallback function if one has been defined
							if (options.closeCallback) {
								options.closeCallback();
							}
							
							return false;
						});
					}
					
					// try to bind the close event to any "closer" elements.
					Sprint.modal.elem.find(".closeModal").bind("click", function() {
						
						//Close the modal
						Sprint.modal.elem.closeModal();

						//Fire off the closeCallback function if one has been defined
						if (options.closeCallback) {
							options.closeCallback();
						}
						
						return false;
					});
				};

				// Don't animate in IE
				if ($.support.opacity) {
					Sprint.modal.elem.fadeIn("fast", afterOpen());
				}
				else {
					Sprint.modal.elem.show(afterOpen());
				}

				// If window was resized, calculate & reset the overlay dimensions
				$(window).bind("resize", function() {
					Sprint.modal.dimensions.documentWidth	= docWidth	= $(document).width();
					Sprint.modal.dimensions.documentHeight  = docHeight = $(document).height();
					Sprint.modal.dimensions.windowWidth	    = winWidth	= $(window).width();
					Sprint.modal.dimensions.windowHeight	= winHeight = $(window).height();
					Sprint.modal.dimensions.bodyWidth	    = bodyWidth	= $("body").width();
					Sprint.modal.dimensions.bodyHeight      = bodyHeight = $("body").height();
					Sprint.modal.dimensions.overlayWidth	= ($.browser.msie && parseInt($.browser.version) < 7) ? bodyWidth : docWidth; //IE uses bodyWidth, everything else uses docWidth
					Sprint.modal.dimensions.overlayHeight	= (docHeight < winHeight) ? winHeight : docHeight;
					Sprint.modal.dimensions.scrollLeft		= $(document).scrollLeft();
					Sprint.modal.dimensions.scrollTop		= $(document).scrollTop();

					var dimensions = Sprint.modal.dimensions;

					$("#modalOverlay").css({
						width:	 dimensions.overlayWidth,
						height:  dimensions.overlayHeight
					}).removeShim().addShim();
					
					//Set the modal's size again
					Sprint.modal.elem.sizeModal(options);
				});
			};

			//If the modal should open after a set amount of time, set the timer and store it in the Sprint.modal object
			if (options.delay > 0) {
				Sprint.modal.timer = setTimeout("showModal()", options.delay);
			}
			else {
				showModal();
			}
		},

		/*
		* closeModal
		*
		* function to close a modal window
		*
		*/
		closeModal: function() {
		
			//Move the content back to it's original location.
			Sprint.modal.originalContent.appendTo("#"+Sprint.modal.content.attr("id")+"_holder");
			
			//Create a function that will fire after the modal window closes that handles some cleanup routines.
			var afterClose = function() {
				Sprint.modal.elem.remove();

				$("#modalOverlay").hide().remove();

				$("#modalOverlay").removeShim(); //Remove the shim for IE

				//Delete the options and elem objects
				delete Sprint.modal.options;
				delete Sprint.modal.elem;
				
				//Free-up the window's resize listener
				$(window).unbind("resize");
			};

			//Don't animate in IE
			if ($.support.opacity) {
				Sprint.modal.elem.fadeOut("fast", afterClose());
			}
			else {
				Sprint.modal.elem.hide(afterClose());
			}
			
			
		},


		/*
		* sizeModal
		*
		* function to resize a modal window
		*
		* @param options: Object, defines the modal's options
		*
		*/
		sizeModal: function(options) {
			
			//Make sure we have some options
			if (!options) {
				var options = Sprint.modal.options; //Fall back to the options that are currently stored in the Sprint namespace
			}

			Sprint.modal.dimensions.scrollLeft = $(document).scrollLeft();
			Sprint.modal.dimensions.scrollTop	= $(document).scrollTop();

			var dimensions = Sprint.modal.dimensions;
			
			// determine the width of the modal based on the modal content
			modalWidth = $(Sprint.modal.content).width();
			
			//Make sure the modal is never wider than the default width
			if (modalWidth > options.width) {
				modalWidth = options.width;
			}
			
			$(".modalChromeRight, .modalContent").css({
				width: modalWidth
			});

			// the left part of the modal is 48px wide, the right is the modal content width + 48px
			var modalRightWidth = modalWidth + 48;
			
			// the modal chrome adds an additional 96px to the width of the modal (margins are 40 pixels, shadows are 8 pixels)
			var modalChromeWidth = modalWidth + 96;

			$(".modalChromeTopRight, .modalChromeBottomRight").css({
				width: modalRightWidth
			});

			var modalChromeHeight = this.height();

			// set the vertical position of the modal based on the modal's height. If the modal itself is taller than the current window's viewport, make sure the top of the modal is in view.
			if (modalChromeHeight > dimensions.windowHeight) {
				var modalMarginTop	= 0;
				var modalPositionTop = dimensions.scrollTop + "px";
			}
			// vertically center the modal in the viewport
			else {
				var modalMarginTop	= ((Math.round(modalChromeHeight / 2) * -1) + dimensions.scrollTop) + "px";
				var modalPositionTop = "50%";
			}

			// set the horizontal position of the modal based on the modal's width. If the modal itself is wider than the current window's viewport, make sure the left edge of the modal is in view.
			if (modalChromeWidth > dimensions.windowWidth) {
				var modalMarginLeft	 = 0;
				var modalPositionLeft = 0;
			}
			// horizontally center the modal in the viewport
			else {
				var modalMarginLeft	 = (Math.round(modalChromeWidth / 2) * -1) + "px";
				var modalPositionLeft = "50%";
			}

			this.css({
				marginTop:   modalMarginTop,
				marginLeft:  modalMarginLeft,
				top:         modalPositionTop,
				left:        modalPositionLeft
			});

			return this;
		},


		/*
		* loadModalContent
		*
		* function to load modal content via AJAX
		*
		* @param ajaxPath: URL to the content to load in
		* @param modal: the modal we're working with (jQuery object)
		*
		*/
		loadModalContent: function(ajaxPath, modal) {
		
			$.ajax({
					
				data: "ajax=true",
				type: "GET",
				url: ajaxPath,
				async: false, //Make this ajax call finish before executing other functions
				
				success: function(data) {
					
					modal.empty().html(data);

				},
				
				error: function(event) {
					alert("error communicating with server\n\nStatus: "+event.status);
				}

			});
			
			//Setup all the default components within the modal
			modal.setupComponents();

			//Resize the modal based on the new content
			Sprint.modal.elem.sizeModal();

		},

		/*
		* tooltip
		*
		* function to create a floating tooltip
		*
		* @param scriptOptions: Object, defines options that will override the defaults
		*
		*/
		tooltip: function(scriptOptions) {
		
			var defaults = {
				timeout:         500,   // integer (milliseconds): the amount of delay before auto-closing the tooltip
				isCapabilities:  false  // boolean: set this value to true if the tooltip is for device capabilities
			};

			var options = $.extend(defaults, scriptOptions);
			
			//Create the tooltip layer if it doesn't already exist							
			var tooltip = $("#tooltip");
		
			//create the tooltip layer if it doesn't already exist
			if (tooltip.length < 1) {
				tooltip = $("<div id=\"tooltip\" class=\"tooltipChrome\"><div class=\"tooltipChromeMiddleRight\"><div class=\"tooltipChromeMiddleLeft\"><div class=\"tooltipChromeMiddle\"><div class=\"tooltipContentArea\"></div></div></div></div></div>");
		
				tooltip.prepend('<div class="tooltipChromeTopRight"><div class="tooltipChromeTopLeft"><div class="tooltipChromeTop">&nbsp;</div></div></div>');
				
				tooltip.append('<div class="tooltipChromeBottomRight"><div class="tooltipChromeBottomLeft"><div class="tooltipChromeBottom">&nbsp;</div></div></div>');
				
				tooltip.append('<div class="tooltipChromeArrow">&nbsp;</div>');
				
				tooltip.hide();
				
				$(".sprint:first").append(tooltip);
			}

			return this.each(function() {
				
				var tooltipScroll; //Variable to hold a timeout that will determine whether or not to scroll the tooltip into view.
			
				// determine the element that the tooltip will be anchored (positioned) to.
				var anchor = $(this);
				
				// prevent the link from going anywhere
				anchor.bind("click", function() {
	
					//If the anchor is inside a label, make sure the click still focuses on the field the label represents.
					if ($(this).parent().is("label")) {
						$("#"+$(this).parent().attr("for")).focus();
					}
					
					return false;
				});
				
				// Show the tooltip on mouseover
				anchor.bind("mouseover", function(e) {
				
					//Don't do anything if this anchor's tooltip is already showing
					if (!anchor.is(".currentTooltip")) {
					
						//Hide the tooltip if it's showing something else
						if (Sprint.tooltip.visible) {
							clearTimeout(Sprint.tooltip.timer);
							hideTooltip(true); //do not animate, just close it right away
						}
						
						//Check to see if this is a capabilities tooltip
						if (anchor.parents("ul.capabilities").length > 0) {
							options.isCapabilities = true;
						}
						else {
							options.isCapabilities = false;
						}
	
						//Find the anchor target that this tooltip links to
						var anchorTarget = anchor.attr("href");
						
						//stip off everything before the "#"
						anchorTarget = anchorTarget.substr(anchorTarget.indexOf("#"));
						
						var tooltipContent = $(anchorTarget).html();
						
						//set the tooltip's content
						tooltip.find(".tooltipContentArea").html(tooltipContent);
						
						//Check to see if this tooltip should be a capabilities tooltip
						if (options.isCapabilities) {
							tooltip.addClass("capabilitiesTooltip");
						}
						else {
							tooltip.removeClass("capabilitiesTooltip");
						}
		
						// initialization for IE
						if (!$.support.opacity) {
							tooltip.show();
							tooltip.hide();
						}
						
						//Position off-screen while we're doing dimension/position calculations
						tooltip.css({
							position: "absolute",
							top: "-9999px",
							left: "-9999px"
						});
						
						//figure out tooltip position
						var anchorTop	 = anchor.offset().top;
						var anchorLeft	 = anchor.offset().left;
						var anchorHeight = anchor.height();
						var anchorWidth	 = anchor.width();
		
						var windowWidth	  = $(window).width();
						var windowHeight  = $(window).height();
		
						var tooltipLeft = 0;
						var tooltipTop = 0;
						var tooltipWidth = tooltip.width();
						var tooltipHeight = tooltip.height();
	
						var mouseX = e.clientX;
						var mouseY = e.clientY;
				
						var leftBound = $("body").offset().left;
						var rightBound = $("body").width();
						
						tooltipLeft = anchorLeft + (anchorWidth / 2) - (tooltipWidth / 2);
						
						var arrowPos = tooltipWidth / 2;
						
						tooltipTop = anchorTop - tooltipHeight + 2; //Add a couple of pixels to pull the tooltip in closer to the anchor
						
						if (options.isCapabilities) {
							tooltipTop = anchorTop + anchorHeight - 2; //Subtract a couple of pixels to pull the tooltip in closer to the anchor
							arrowPos = arrowPos - 13; //Offset by half of the arrow width
						}
						else {
							arrowPos = arrowPos - 10; //Offset by half of the arrow width
						}
						
						//Check to make sure the tooltip will appear in the window's current viewport.
						if (tooltipLeft < leftBound) {
							var tooltipOffset = (Math.abs(tooltipLeft) - leftBound) + 20; //Add 20 to give it some breathing room at the left edge of the window
							tooltipLeft = tooltipLeft + tooltipOffset;
							arrowPos = arrowPos - tooltipOffset;
						}
						else if ((tooltipLeft + tooltipWidth) > rightBound) {
							var tooltipOffset = (rightBound - (tooltipLeft + tooltipWidth)) - 20; //Subtract 20 to give it some breathing room at the right edge of the window
							
							tooltipLeft = tooltipLeft + tooltipOffset;
							arrowPos = arrowPos - tooltipOffset;
						}
						
						// set the tooltip's position based on the above calculations
						tooltip.css({
							top:   tooltipTop,
							left:  tooltipLeft
						});
						
						//set the z-index based on whether or not there's a modal window present
						if ($("#modalHolder").length > 0) {
							tooltip.css("zIndex", 900);			
						}
						else {
							//Reset z-index
							tooltip.css("zIndex", "")
						}
						
						// set the left position for the tooltip's arrow.
						tooltip.find(".tooltipChromeArrow").css({
							left: arrowPos
						});
		
						//Clear any current tooltip animations
						tooltip.stop(true, true);
						
						function afterShow() {
						
							tooltipScroll = setTimeout(function() {
								//If it's not a capabilities tooltip, and the tooltip isn't fully visible in the current viewport, scroll to the top of the tooltip.
								if (!options.isCapabilities) {
									var currentScrollTop = 0;
									
									var htmlScrollTop = $("html").scrollTop();
									var bodyScrollTop = $("body").scrollTop();
									
									//Determine the current scroll top position by checking both the html & body element's scrollTop positions (some browsers use html, some use body).
									if (htmlScrollTop > 0 || bodyScrollTop > 0) {
										if (htmlScrollTop > 0) {
											currentScrollTop = htmlScrollTop;
										}
										else if (bodyScrollTop > 0) {
											currentScrollTop = bodyScrollTop;
										}
									}
									
									//If the top of the tooltip isn't already in view, scroll to it.
									if (tooltip.offset().top < currentScrollTop) {
										tooltip.scrollTo({speed: "slow"});
									}
								}
							}, 100); //Wait a little bit before checking the scroll position
						}
		
						//Show the tooltip!
						if ($.support.opacity) {
							tooltip.fadeIn("fast", afterShow);
						}
						else {
							// IE doesn't handle transparent PNGs and opacity changes well.
							tooltip.show();
							afterShow();
						}
						
						Sprint.tooltip.visible = true;
						anchor.addClass("currentTooltip");
	
						//bind the mouseout event to hide the tooltip
						anchor.bind("mouseout", function() {			
						
							Sprint.tooltip.timer = setTimeout(function() {
								hideTooltip();
							}, 250);
							
						});
	
						//rolling over the tooltip will cancel the delayed fade out
						tooltip.bind("mouseover", function() {
							clearTimeout(Sprint.tooltip.timer);
						});
						
						//rolling out of the tooltip will start the delayed fadeout timer again.
						tooltip.bind("mouseout", function() {
						
							Sprint.tooltip.timer = setTimeout(function() {
								hideTooltip();
							}, 250);
							
						});
						
					}
					else {
						//current tooltip is already showing, clear the timeout so that the tooltip won't fade out.
						clearTimeout(Sprint.tooltip.timer);
					}
	
				});
				
				function hideTooltip(noAnimation) {
					
					if (noAnimation || !$.support.opacity) {
						// IE doesn't handle transparent PNGs and opacity changes well.
						// Do not animate the hiding of the tooltip
						$("#tooltip").hide();
					}
					else {
						$("#tooltip").fadeOut("normal");
					}
					
					Sprint.tooltip.visible = false;
					$("a.currentTooltip").removeClass("currentTooltip").unbind("mouseout");
					$("#tooltip").unbind("mouseover");
					$("#tooltip").unbind("mouseout");
					
					clearTimeout(tooltipScroll); //Prevent auto-scroll if the user rolls off the tooltip.
					
				}

			});

		},

		/*
		* shade
		*
		* function to create a shade (usually used for Help)
		*
		* @param scriptOptions: Object, defines options that will override the defaults
		*
		*/
		shade: function(scriptOptions) {
		
			var defaults = {
				width:        380,      // integer or string: default shade width (in pixels if defined as an integer)
				isCommented:  false,    // boolean: set this value to true if the modal content is commented out
				closedText:   Sprint.content.shade.closedText[Sprint.currentLanguage],   // string: The text of the shade tab when closed
				openedText:   Sprint.content.shade.openedText[Sprint.currentLanguage],  // string: The text of the shade tab when open
				speed:        "fast"    // string: The speed of the shade animation, can also be an integer.
			};

			var options = $.extend(defaults, scriptOptions);

			if (this.length == 0) return this;
			
			//Store the Shade close function in the Sprint namespace
			Sprint.shade.close = function() {
				$(".shade").each(function() {
				   close($(this)); 
				});
				unbindEvents();
			};
			
			return this.each(function() {
				if (options.isCommented) {
					// uncomment the shade content
					shadeContent = $(this).html().replace(/^\s*<!--\s*|\s*-->\s*$/mg, "");
					$(this).html(shadeContent);
				}
				
				// create the HTML for the shade
				$(this).html("<div class=\"shadeContainer\"><div class=\"shadeContent\">" + $(this).html() + "</div><div class=\"headingTabLink\"><a href=\"#\">" + options.closedText + "</a></div></div>");
				
				//Set the width
				$(this).find(".shadeContainer").width(options.width);
				
				//Prevent clicks on the shade itself (unless it's the tab) from bubbling up (which would cause the shade to close)
				$(this).find("div:not(.headingTabLink)").bind("click", function(event) {
					event.stopPropagation();
				});
				
				//Event handler for the shade open/close tab
				$(this).find(".headingTabLink").bind("click", function(event) {
					event.stopPropagation(); //prevent bubbling
					
					var shade = $(this).parents(".shade");
					
					//If user is clicking on the shadeTab for the shade that's currently open... close it
					if (shade.find(".shadeContent").css("display") == "block") {
						close(shade);
						unbindEvents();
					}
					//If not, open the requested shade (& close any that may already be open)
					else {
						Sprint.shade.close();
						open(shade);
						bindEvents();
					}
					event.preventDefault(); // don't follow the link
				});
			});

			// event handler that will be set to close the current shade when a user clicks anywhere outside it (unless it's on another shade).
			function handleClick(event) {
				if (!Sprint.shade.isAnimating) {
					event.stopPropagation();
					Sprint.shade.close();
					unbindEvents();
				}
			};
			
			function bindEvents() {
				$("html, body, div").not($(".shade, .shade div")).bind("click", handleClick);
			};
			
			function unbindEvents() {
				$("html, body, div").not($(".shade, .shade div")).unbind("click", handleClick);
			};
			
			function close(shade) {
				if (!Sprint.shade.isAnimating) {
					Sprint.shade.isAnimating = true;
					shade.find(".shadeContent").stop().slideUp(options.speed, function() {
						Sprint.shade.isAnimating = false;				
						shade.find(".headingTabLink a").html(options.closedText);
					});
				}
			};
			
			function open(shade) {
				if (!Sprint.shade.isAnimating) {
					Sprint.shade.isAnimating = true;
					shade.find(".shadeContent").stop().slideDown(options.speed, function() {
						Sprint.shade.isAnimating = false;
						shade.find(".headingTabLink a").html(options.openedText);
					});
				}
			};
		},

		/*
		* accordion
		*
		* function to create accordion objects
		*
		* @param scriptOptions: Object, defines options that will override the defaults
		*
		*/
		accordion: function(scriptOptions) {
		
			var defaults = {
				speed:                   "slow",  // string: The speed of the accordion animation, can also be an integer.
				toggleButton:            false,   // boolean: set this value to true if the accordion items should have toggle buttons.
				toggleButtonClosedText:  Sprint.content.accordion.toggleButtonClosedText[Sprint.currentLanguage],  // string: Toggle button text for when an accordion item is closed.
				toggleButtonOpenedText:  Sprint.content.accordion.toggleButtonOpenedText[Sprint.currentLanguage]  // string: Toggle button text for when an accordion item is open.
			};
			
			var options = $.extend(defaults, scriptOptions);
		
			return this.each(function() {
			
				var currentAccordion = $(this);
			
				//Hide accordion content layers if the parent item isn't selected
				currentAccordion.find(".accordionContent").each(function() {
					if (!$(this).parent().is(".accordionItemSelected")) {
						$(this).hide();
					}
				});
				
				//Check to make sure that this accordion has at least one item open. If not, open the first one.
				if (currentAccordion.find(".accordionItemSelected").length < 1) {
					currentAccordion.find(".accordionContent:first").show().parent().addClass("accordionItemSelected");					
				}
				
				//wrap accordion headers in anchor tags
				currentAccordion.find(".accordionHeader").each(function() {
					$(this).html("<a href=\"#\">"+$(this).html()+"</a>");
				});
				
				//setup event handlers for when the accordion item headers are clicked.
				currentAccordion.find(".accordionHeader").bind("click", function() {
					
					//Only perform actions if this item is NOT selected
					if (!$(this).parent().hasClass("accordionItemSelected")) {
					
						//Unselect the currently open accordion item
						currentAccordion.find(".accordionItemSelected").find(".accordionContent").slideUp(options.speed);
						currentAccordion.find(".accordionItemSelected").removeClass("accordionItemSelected");
						
						//Open whichever accordion item was selected
						var selectedItem = $(this).parent();
						
						selectedItem.addClass("accordionItemSelected");
						selectedItem.find(".accordionContent").slideDown(options.speed);
					
					}
					
					//If there are toggle buttons for this accordion, update their text.
					if (options.toggleButton) {
						currentAccordion.find(".accordionItem a.accordionToggle").html(options.toggleButtonClosedText);
						currentAccordion.find(".accordionItemSelected a.accordionToggle").html(options.toggleButtonOpenedText);
					}
					
					return false;
					
				});
				
				//If the accordion items are supposed to have open/close toggle buttons, create them and bind some events
				if (options.toggleButton) {
				
					currentAccordion.find(".accordionItem").each(function() {
						
						var toggleButtonText = options.toggleButtonClosedText;
						
						if ($(this).hasClass("accordionItemSelected")) {
							toggleButtonText = options.toggleButtonOpenedText;
						}
						
						var toggleButton = $("<a href=\"#\" class=\"accordionToggle\">"+toggleButtonText+"</a>");
						
						toggleButton.bind("click", function() {
							$(this).parents(".accordionItem").find(".accordionHeader").trigger("click");
							return false;
						});
						
						$(this).append(toggleButton);
					});
				
				
				}
				
			});
		},
		
		/*
		* horizontalAccordion
		*
		* function to create a horizontal accordion
		*
		*/
		horizontalAccordion: function() {
	
			if (this.length == 0) return;
			
			var accordion = $(this).get(0);
			
			accordion.isAnimating = false;
			
			accordion.selectItem = function(index) {
			
				// check to see if there's a currently open item
				var hasCurrent = ($(this).find(".current").length > 0);
				
				var accordion = this;
				
				// if there's a current item open
				if (hasCurrent)
				{
					var selectedItem = $(this).children(".accordionItem").get(index);
					var currentItem  = $(this).children(".current").get(0);
					var minWidth     = $(selectedItem).width();
					var maxWidth     = $(currentItem).width();
					var minBGColor   = $(selectedItem).css("background-color");
					var maxBGColor   = $(currentItem).css("background-color");
					
					//Fade out the current item's content, shrink the width, then fade it back in again
					$($(currentItem).children().get(0)).fadeOut("fast", function() {
						//Shrink the width, then fade in the content
						$(currentItem).animate(
							{
								width: minWidth,
								opacity: 0
							},
							"slow",
							function() {
								$($(currentItem).children().get(0)).fadeIn("fast");
								$(currentItem).removeClass("current");
							}
						);
					});
					
					//Fade out the selected item, make it wider, then fade it back in again
					$($(selectedItem).children().get(0)).fadeOut("fast", function() {
						//increase the width
						$(selectedItem).animate(
							{
								width: maxWidth
							},
							"slow",
							function() {
								$(accordion).find(".accordionItem").css({
									opacity: "",
									width: "",
									backgroundColor: "",
									float: ""
								});
								
								$($(selectedItem).children().get(0)).fadeIn("fast");
								
								accordion.setClasses(index);
							}
						);
					});
					
					//Prevent the last item from moving around while the animation is taking place
					$(this).find(".accordionItem:last-child").css({
						float: "right"
					});
				}
				// Nothing currently selected, set up the accordion's initial state
				else {
					accordion.setClasses(index);
				}
			};
			
			accordion.setClasses = function(index) {
				// apply classes for each accordionItem
				$(this).children(".accordionItem").each(function(i) {
				
					//Force all items to be non-current, and remove their orientation
					$(this).removeClass("current").removeClass("leftSide").removeClass("rightSide");
					
					//Figure out the new orientation of each accordion item to determine the "Learn More" arrow's positioning.
					/*
					
						1. If the item index is less than the index of the item that's currently open, it gets a class of leftSide as it will be TO THE LEFT of the open item (and thus, the "Learn More" arrow will point to the right)
						
						2. If the item index is greater than the index of the item that's currently open, it gets a class of rightSide as it will be TO THE RIGHT of the open item (and thus, the "Learn More" arrow will point to the leftt)
						
						3. If the item index is equal to the item that's currently open, it gets a calss of current.
					
					*/
					if (i < index) {
						$(this).addClass("leftSide");
					}
					else if (i > index) {
						$(this).addClass("rightSide");
					}
					else {
						$(this).addClass("current");
					}
				});
				
				accordion.setZIndexes();
			};
			
			// This function is necessary to correct for IE's incorrect z-index handling. It also makes sure that the items that aren't open (and have "Learn More" arrows protruding from them) have a higher z-index than the open item. Also, if there are more than 1 of any particular side (2+ rightSiders or 2+ leftSiders), it makes sure they stack correctly as well.
			accordion.setZIndexes = function() {
				var itemCount = $(this).find(".accordionItem").length;
				var leftSiders = $(this).find(".accordionItem.leftSide");
				var rightSiders = $(this).find(".accordionItem.rightSide");
				
				//z-index for each individual leftSider - the leftmost item has the highest z-index
				$(leftSiders).each(function(i) {
					$(this).css({
						zIndex: (leftSiders.length - i) + 1
					});
				});
				
				//z-index for each individual rightSider - the rightmost item has the highest z-index
				$(rightSiders).each(function(i) {
					$(this).css({
						zIndex: i + 1
					});
				});
				
				//current item gets a z-index of 0 to make sure that left and right siders get stacked on top of it.
				$(this).find(".accordionItem.current").each(function(i) {
					$(this).css({
						zIndex: 0
					});
				});
				
				// after 1/4 second, set the animating flag to false, this will allow the other "learn more" links to be clickable again
				setTimeout(function() {
					accordion.isAnimating = false;
				}, 250);
			};
			
			//set the base classes for this accordion and its items
			$(this).addClass("horizontalAccordion");
			$(this).children().addClass("accordionItem");
			
			// set the heights and widths
			var accordionItemPaddingTop    = parseInt($(this).find(".accordionItem:first-child").css("padding-top"));
			var accordionItemPaddingBottom = parseInt($(this).find(".accordionItem:first-child").css("padding-bottom"));
			
			//Add a class of current to all items - this will cause all items to be their final width/height and give us the ability to accurately get their dimensions
			$(this).find(".accordionItem").addClass("current");
			
			//Temporarily set the accordion container to a huge width, so that we can get accurate height measurements
			$(this).css({
				position: "absolute",
				width: "9999px"
			});
			
			//Store this accordion's height
			accordion.accordionHeight = $(this).height();
			
			//Give all the accordion items a class of leftSide to make them all their thin versions
			$(this).find(".accordionItem").removeClass("current").addClass("leftSide");
			
			//Now that we have the height of all the thin versions of the accordion items, check to see if it's greater than the height of the wide versions, if so, store this as the new accordion height
			if ($(this).height() > accordion.accordionHeight) {
				accordion.accordionHeight = $(this).height();
			}
			
			//set the height of all the accordion items to the height we've determined above, minus the padding
			$(this).find(".accordionItem").each(function() {
				$(this).height(accordion.accordionHeight - accordionItemPaddingTop - accordionItemPaddingBottom);
			});
			
			//reset the accordion container width, now that we've gotten our accurate height measurements
			$(this).css({
				position: "",
				width: ""
			});
			
			// select the first accordion item
			accordion.selectItem(0);
			accordion.accordionWidth = $(this).width();
			
			// this calcWidth business is for IE6.
			var calcWidth = 0;
			function getNum(val) {
				var num = parseInt(val);
				if (isNaN(num)) num = 0;
				return num;
			}
			
			$(this).find(".accordionItem").each(function () {
				calcWidth = calcWidth + getNum($(this).css("border-left"))  + getNum($(this).css("border-right"));
				calcWidth = calcWidth + getNum($(this).css("padding-left")) + getNum($(this).css("padding-right"));
				calcWidth = calcWidth + getNum($(this).css("margin-left"))  + getNum($(this).css("margin-right"));
				calcWidth = calcWidth + getNum($(this).css("width"));
			});
			
			accordion.accordionWidth = calcWidth + 2; // +2 for the left/right border (1px each)
			
			//set the accordion dimensions to the calculated width/height
			$(this).width(accordion.accordionWidth);
			$(this).height(accordion.accordionHeight);

			// bind a click even to the "Learn More" tabs
			$(this).find(".moreTab a").bind("click", function(e) {			
			
				e.preventDefault();
				
				//Don't bother doing any of this if there's already an animation taking place
				if (!accordion.isAnimating) {
					var index;
					var thisItem = $(this).parents(".accordionItem").get(0);
					
					//determine the selected index (the one the user wants to open)
					$(this).parents(".horizontalAccordion").children(".accordionItem").each(function(i) {
						if (this == thisItem) {					
							index = i;
						}
					});
					
					//set the animation flag to true to tell other functions that the accordion is currently in the middle of an animation
					accordion.isAnimating = true;
				
					//Open up the selected item
					$(this).parents(".horizontalAccordion").get(0).selectItem(index);
				}
				
			});
		},

		/*
		* tabbedContent
		*
		* function to create tabbed content sections
		*
		* @param scriptOptions: Object, defines options that will override the defaults
		*
		*/
		tabbedContent: function(scriptOptions) {
			
			var defaults = {
				indexClass:   "contentTabs",  // string: The CSS class that the tab index will have.
				afterSwitch:  null            // function: this function will be run on each tab switch

			};

			var options = $.extend(defaults, scriptOptions);

			return this.each(function() {

				var tabbedSection = $(this);
		
				//hide all the tabbed content		
				tabbedSection.children(".tabContent").hide();
				
				//generate the tab index
				var tabIndex = $("<ul class=\"tabIndex\"></ul>");
				tabIndex.addClass(options.indexClass);
		
				tabbedSection.children(".tabContent").each(function(i) {
					
					var tabID = $(this).children(".sectionTitle").attr("id");

					var tabTitle = $(this).children(".sectionTitle").text();

					$(this).children(".sectionTitle").attr("id", "").hide();					
					
					//create an index item for it
					var newTab = $("<li id=\""+tabID+"\"><a href=\"#\">"+tabTitle+"</a></li>");
					newTab.appendTo(tabIndex);
					
				});
				
				//Set classes on first and last tab
				tabIndex.children("li:first").addClass("first");
				tabIndex.children("li:last").addClass("last");
				
				//Insert the tab index into the dom directly before the tabWrapper element
				tabIndex.insertBefore(tabbedSection);
				
				tabIndex.children("li").bind("click", function() {
					
					//Get selected index and switch tabs.
					var selectedIndex = tabIndex.children("li").index(this);
					
					selectTab(selectedIndex);
					
					return false;
					
				});
				
				//store the number of parent .tabContent layers
				var numParents = $(this).parents(".tabContent").length;
				
				selectTab(0);
				
				function selectTab(selectedIndex) {

					var selectedTab = $(tabIndex.children("li")[selectedIndex]);
					
					//un-select any selected tabs and remove the before/after classes
					tabIndex.children("li").removeClass("selected");
					tabIndex.children("li").removeClass("firstSelected");
					tabIndex.children("li").removeClass("beforeSelected");
					tabIndex.children("li").removeClass("afterSelected");
					
					//Select the tab with the clicked index.
					selectedTab.addClass("selected");
					
					//add classes to the tabs directly before/after the selected tab
					selectedTab.prev().addClass("beforeSelected");
					selectedTab.next().addClass("afterSelected");
					
					//add a unique class to the first item if the first item of any tab index is selected
					$(".tabIndex li.first.selected").addClass("firstSelected");
					
					//hide any content
					tabbedSection.children(".tabContent").hide().removeClass("selected");
					
					//show the selectedIndex content
					$(tabbedSection.children(".tabContent")[selectedIndex]).show().addClass("selected");
					
					if (options.afterSwitch) {
						options.afterSwitch();
					}
					
				}

			});
		
		},

		/*
		* scrollTo
		*
		* function that automatically scrolls to a certain element
		*
		* @param scriptOptions: Object, defines options that will override the defaults
		*
		*/
		scrollTo: function(scriptOptions) {
		
			var defaults = {
				speed:             "fast",  // string: The speed of the scrolling animation, can also be an integer.
				topOffset:         5,       // integer: Number of pixels above a given object that the scrollTo function will stop.
				horizontalScroll:  false,   // boolean: set this value to true if you want horizontal auto-scroll.
				verticalScroll:    true,    // boolean: set this value to false if you don't want vertical auto-scroll.
				afterScroll:       null     // function: this function will run when scrolling completes.
			};

			var options = $.extend(defaults, scriptOptions);

			var callbackFinished = false; //Flag to determine whether or not the callback function has been fired
			
			//find the target top/left positions and scroll
			var targetScrollTop = $(this).offset().top - options.topOffset;
			var targetScrollLeft = $(this).offset().left;
			
			var animationValues = {};
			
			if (options.verticalScroll) {
				animationValues.scrollTop = targetScrollTop;
			}
			
			if (options.horizontalScroll) {
				animationValues.scrollLeft = targetScrollLeft;
			}
			
			$("html, body").animate(animationValues, options.speed, function() {
			
				//Because we're calling the animate function on 2 elements (html & body - to ensure that it works in all browsers), we need to make sure the callback only fires once.			
				if (!callbackFinished) {
					if (options.afterScroll) {
						options.afterScroll();
					}
					callbackFinished = true;
				}
			});
			
			return this;
			
		},

		/*
		* disclosure
		*
		* function to create disclosure components
		*
		* @param scriptOptions: Object, defines options that will override the defaults
		*
		*/
		disclosure: function(scriptOptions) {

			var defaults = {
				openedText:      Sprint.content.disclosure.openedText[Sprint.currentLanguage],   // string: The text of the disclosure toggle when the disclosure is open
				closedText:      Sprint.content.disclosure.closedText[Sprint.currentLanguage],    // string: The text of the disclosure toggle when the disclosure is closed
				speed:           "medium",  // string: The speed of the animation, can also be an integer.
				startClosed:     false,     // boolen: set this value to true if the disclosure should be closed by default
				titleClickable:  true,      // boolen: set this value to false if the disclosure shouldn't be opened/closed by clicking on the title
				openCallback:    null,      // function: this callback will be run after the disclosure opens
				closeCallback:   null       // function: this callback will be run when the disclosure closes
			}

			//Extend the default options with any that are passed in
			var options = $.extend(defaults, scriptOptions);

			return this.each(function() {
				
				var disclosure = $(this);
				
				var toggleButtonText = options.openedText;
				
				if (options.startClosed || disclosure.hasClass("disclosureClosed")) {
					//start off closed
					disclosure.addClass("disclosureClosed");
					disclosure.find(".disclosureContent:first").hide();
					toggleButtonText = options.closedText;
				}
				else {
					//start off open
					disclosure.addClass("disclosureOpen");
				}
				
				//give the discolsure a toggle button
				var toggleButton = $("<a href=\"#\" class=\"disclosureToggle\">"+toggleButtonText+"</a>");
				disclosure.append(toggleButton);
				
				toggleButton.bind("click", function() {
					
					if (disclosure.is(".disclosureClosed")) {
						//Open it					
						disclosure.find(".disclosureContent:first").slideDown(options.speed, options.openCallback);
						disclosure.addClass("disclosureOpen");
						disclosure.removeClass("disclosureClosed");
						$(this).html(options.openedText);
					}
					else {
						//Close	it
						disclosure.find(".disclosureContent:first").slideUp(options.speed, options.closeCallback);
						disclosure.addClass("disclosureClosed");
						disclosure.removeClass("disclosureOpen");
						$(this).html(options.closedText);
					}
					
					return false;
				});
				
				if (options.titleClickable) {
					//Let users click on the heading element to open/close the disclosure
					disclosure.children(".disclosureTitle").css("cursor", "pointer");
					
					disclosure.children(".disclosureTitle").bind("click", function() {
					
						disclosure.find(".disclosureToggle").trigger("click");
					
					});
				}
				
			});
		
		},

		/*
		* addRoundedCorners
		*
		* function to add additional divs to a container to give it rounded corners
		*
		*/
		addRoundedCorners: function() {
		
			return this.each(function() {
				$(this).append("<div class=\"roundCorner roundCornerTopLeft\"></div><div class=\"roundCorner roundCornerTopRight\"></div><div class=\"roundCorner roundCornerBottomLeft\"></div><div class=\"roundCorner roundCornerBottomRight\"></div>");
			});
		
		},
		
		/*
		* addDropShadows
		*
		* function to add additional divs to a container to give it a drop shadow
		*
		*/
		addDropShadows: function() {
		
			return this.each(function() {
				$("<div class=\"dropShadow\"></div>").width($(this).outerWidth() - 4).appendTo($(this));
			});
			
		},
		
		/*
		* equalizeHeights
		*
		* function to equalize the heights of a set of elements
		*
		*/
		equalizeHeights: function() {
		
			var finalHeight = 0;
			
			$(this).each(function() {
				
				if ($(this).height() > finalHeight) {
					finalHeight = $(this).height();
				}
				
			});
			
			$(this).height(finalHeight);
			
			return this;
			
		},
		
		/*
		* createHighResButtons
		*
		* function to convert low-res input buttons into high-res anchors that look like buttons.
		*
		* @param scriptOptions: Object, defines options that will override the defaults
		*
		*/
		createHighResButtons: function(scriptOptions) {

			var defaults = {
				attachClickEvent:  true  // boolean: set this value to false if you don't want the new high-res button's click event to trigger the low-res version's click.
			};

			var options = $.extend(defaults, scriptOptions);
		
			return this.each(function() {
			
				//Replace the input button with an anchor and attach the anchor's click event to the button's event.
				var theButton = $(this);
				
				//TEMPORARY check for examples
				if (!theButton.parent().hasClass(".doNotConvert")) {
					
					var buttonText;
					var	buttonClass = theButton.attr("class")+"_converted";
					var	buttonID = theButton.attr("id");

					if (theButton.is("a")) {
						buttonText = theButton.html();
						
						//Wrap the current button's HTML in some spans						
						theButton.html("<span><span><span><span>"+buttonText+"</span></span></span></span>");
						
						//Remove any non-converted button classes.
						theButton.removeClass("button1");
						theButton.removeClass("button2");
						theButton.removeClass("button3");
						theButton.removeClass("button4");

						//Set the new (converted) button class
						theButton.addClass(buttonClass);
					}
					else {
						//Assume the button is an input button						
						//Hide the button
						theButton.hide();
						
						//Get the button text, class and ID
						buttonText = theButton.val();
						
						//Give the button a new ID
						theButton.attr("id", buttonID+"_original");
						
						//Create an anchor with a bunch of spans and give it the same ID & Class that the input button had.
						var newAnchor = $("<a href=\"#\" id=\""+buttonID+"\" class=\""+buttonClass+"\"><span><span><span><span>"+buttonText+"</span></span></span></span></a>");
						
						if (options.attachClickEvent) {
							//Make the click event of this new anchor fire off the click event of the button.
							newAnchor.bind("click", function() {
								theButton.trigger("click");
								return false;
							});
						}
						
						newAnchor.insertBefore(theButton);
					}
					
				}
				
			});
			
		},
		
		/*
		* verticalCenter
		*
		* function to center an element vertically in relation to its parent.
		*
		*/
		verticalCenter: function() {
			return this.each(function () {
				var parentHeight = parseInt($(this).parent().height(), "10");
				var parentPaddingTop = parseInt($(this).parent().css("padding-top"), "10");
				var parentPaddingBottom = parseInt($(this).parent().css("padding-bottom"), "10");
				var height = parseInt($(this).height(), "10");
				var paddingTop = parseInt($(this).css("padding-top"), "10");
				var paddingBottom = parseInt($(this).css("padding-bottom"), "10");
				var difference = (parentHeight + parentPaddingTop + parentPaddingBottom) - (height + paddingTop + paddingBottom);
				var adjustment = parseInt(difference / 2, "10");
				var marginTop = parseInt($(this).css("margin-top"), "10");
				if (isNaN(marginTop)) marginTop = 0;
				if (isNaN(adjustment)) adjustment = 0;
				$(this).css("margin-top", marginTop + adjustment);
			});		
		},
		
		
		/*
		* starRating
		*
		* function to create 5-star rating systems from radio buttons.
		*
		*/
		starRating: function() {
		
			return this.each(function() {
			
				var currentRater = $(this);
				
				currentRater.addClass("starRating");
				
				//Check to see if there is a currently selected value, if so, add the appropriate class to the fieldset.
				currentRater.find("input[type=radio]").each(function() {
					if ($(this).attr("checked")) {
						$(this).parent().addClass("rating"+$(this).val());
					}
				});
	
				//Add anchors around the label text to enable tabbing through the star ratings for keyboard users
				currentRater.find("label").each(function(i) {
					var currentText = $(this).text();
					$(this).html('<a href="#">'+currentText+'</a>');
					$(this).addClass("rate"+(i+1));
				});
	
				//Add event handlers to the labels so that on click it selects the corresponding radio button and "saves" the star rating by applying a class to the fieldset.
				currentRater.find("label").bind("click", function() {
					
					//First, make sure the corresponding radio button gets selected.
					var selectedRadio = $(this).attr("for");
			
					$('#'+selectedRadio).attr("checked", true);
			
					//Get the corresponding rating (number) value for this radio button
					var ratingVal = $('#'+selectedRadio).val();
					
					//Remove any previous ratings
					currentRater.removeClass("rating1 rating2 rating3 rating4 rating5");
					
					//Set the selected rating
					currentRater.addClass("rating"+ratingVal);
					
					//Remove focus from the selected rating's anchor tag.
					$(this).find("a").trigger("blur");
					
					return false;
					
				});
			
			});
		
		},
		
		
		/*
		* addShim
		*
		* function to add an iframe shim to block out form elements (windowed elements) in IE
		*
		*/
		addShim: function() {
		
			//Only run this function for IE6-
			if ($.browser.msie && parseInt($.browser.version) < 7) {

				return this.each(function() {

					var el = $(this);
					
					//Check to see if there's already a shim for this element, if there is, don't add any new ones.
					var hasShim = false;
					
					//Loop through the current shims and determine if the current domNode already has a shim present.
					$(Sprint.ieShims.elem).each(function() {
	
						if ($(this.domNode)[0] == el[0]) {
							hasShim = true;
						}
	
					});
					
					if (!hasShim) {
						Sprint.ieShims.currentShims++; //Increment the number of shims (to generate unique ids)
						
						var shimID = Sprint.ieShims.currentShims;
						
						//Get the current element's position on the page and its dimensions, then create a shim that's the same size and place it behind the current element.
						var props = {				
							offset: el.offset(),
							width: el.outerWidth(),
							height: el.outerHeight()
						}
						
						var newShim = $("<iframe src=\"javascript:false;\" id=\"IESHIM_"+shimID+"\"></iframe>").css({
							position:  "absolute",
							top:       props.offset.top+"px",
							left:      props.offset.left+"px",
							margin:    0,
							padding:   0,
							width:     props.width+"px",
							height:    props.height+"px",
							border:    "none",
							opacity:   0
						});
						
						newShim.appendTo("body");
						
						//Save this shim's information at the end of the Sprint.ieShims.elem array
						Sprint.ieShims.elem.push({
							id: "IESHIM_"+shimID,
							domNode: el
						});
	
					}
	
				});
			}

			return this;

		},
		
		
		/*
		* removeShim
		*
		* function to remove the iframe shim
		*
		*/
		removeShim: function() {
			//Only run this function for IE6-
			if ($.browser.msie && parseInt($.browser.version) < 7) {
	
				return this.each(function() {
				
					var el = $(this);
					
					//Find the shim for this item in the Sprint.ieShims.elem array
					var targetShim;
					
					$(Sprint.ieShims.elem).each(function(i) {
						if ($(this.domNode)[0] == el[0]) {
							targetShim = $("#"+this.id);
	
							//Remove the shim details from the array
							Sprint.ieShims.elem[i] = {id: "", domNode: null};
						}
					});
	
					//Remove the shim
					if (targetShim) {
						targetShim.remove();
					}
				});
				
			}
			
			return this;
			
		},
		
		
		/*
		* carousel
		*
		* function to create carousels
		*
		* @param scriptOptions: Object, defines options that will override the defaults
		*
		*/
		carousel: function(scriptOptions) {
		
			var defaults = {
				scroll:          5,           //integer: the number of items to scroll in a single motion
				speed:           "slow",      //string: the speed of the carousel animation (can also be an integer)
				baseClass:       "carousel",  //string: the base CSS class of the carousel container
				visibleItems:    5,           //integer: the number of items that are visible in the carousel
				numRows:         1,           //integer: the number of rows in the carousel
				hideButtons:     false,       //boolean: set this value to true if the buttons should disappear when all items fit
				nextButtonText:  Sprint.content.carousel.nextButtonText[Sprint.currentLanguage],     //string: the text for the "Next" link
				prevButtonText:  Sprint.content.carousel.prevButtonText[Sprint.currentLanguage]  //string: the text for the "Previous" link
			};
			
			var options = $.extend(defaults, scriptOptions);
		
			return this.each(function() {
			
				//add base class to allow all carousels to inherit CSS styles
				$(this).addClass(options.baseClass);
				
				var carouselList = $(this).find("ul");
				
				var numberOfItems = carouselList.find("li").length;
				
				var itemWidth = carouselList.find("li").width();
				var itemOuterWidth = carouselList.find("li").outerWidth(true); //get the outer width of each list item including its margins
				
				var rightOffset = 0;
				
				//Determine the correct rightOffset amount to use to account for the list item right margins
				if (!isNaN(parseInt(carouselList.find("li").css("marginRight")))) {
					rightOffset = parseInt(carouselList.find("li").css("marginRight"));
				}
				
				if (itemWidth == 0) {
					//Check to see if there's a CSS width
					if (isNaN(parseInt(carouselList.find("li").css("width")))) {
						itemWidth = 0;
					}
					else {						
						itemWidth = parseInt(carouselList.find("li").css("width"));
						
						//check if there's any outerWidth - if so, add the value to the itemWidth
						if (itemOuterWidth > 0) {
							itemWidth = itemWidth + itemOuterWidth;
						}
					}
				}
				
				//if the item's outerWidth is still greater than the item's width - use the outerWidth
				if (itemOuterWidth > itemWidth) {
					itemWidth = itemOuterWidth;
				}
				
				var totalWidth = Math.ceil(numberOfItems / options.numRows) * itemWidth;
				
				carouselList.wrap("<div class=\"carouselClip\" />");
				
				var clipWidth = itemWidth * options.visibleItems - rightOffset;
				
				//Set the carousel and clip widths
				$(this).width(clipWidth);
				$(this).find(".carouselClip").width(clipWidth);
				
				var prevLink = $("<a href=\"#\" class=\"prevLink\">"+options.prevButtonText+"</a>");
				var nextLink = $("<a href=\"#\" class=\"nextLink\">"+options.nextButtonText+"</a>");
				
				$(this).append(prevLink);
				$(this).append(nextLink);
				
				carouselList.width(totalWidth);
			
				function getCurrentLeftPos() {
					var currentLeft = parseInt(carouselList.css("left"));

					if (isNaN(currentLeft)) {
						currentLeft = 0;
					}
					
					return currentLeft;
				}

				if (options.hideButtons) {
					//Hide the buttons if the number of items (on a single row) is equal to the number shown
					if (Math.ceil(numberOfItems / options.numRows) <= options.visibleItems) {
						
						//Hide buttons and left-align everything
						nextLink.hide();
						prevLink.hide();
						$(this).css("paddingLeft", 0);
						$(this).css("marginLeft", 0);
						$(this).addClass("noButtons");
					}
					else {
						//Reset CSS properties (that may have been set by filtering)
						$(this).css("paddingLeft", "");
						$(this).css("marginLeft", "");
						$(this).removeClass("noButtons");
					}
				}
				
				function setButtons() {
				
					//Unlock the buttons
					prevLink.removeClass("locked");
					nextLink.removeClass("locked")
				
					var currentLeft = getCurrentLeftPos();
					
					//Previous button: disable if left position is at 0
					if (currentLeft == 0) {
						prevLink.removeClass("prevLink").addClass("prevLinkDisabled");
					}
					else {
						prevLink.removeClass("prevLinkDisabled").addClass("prevLink");
					}

					//Next button: disable if right-most item is fully visible
					if ((Math.abs(currentLeft) + clipWidth + rightOffset) == totalWidth || clipWidth > totalWidth) {
						nextLink.removeClass("nextLink").addClass("nextLinkDisabled");
					}
					else {
						nextLink.removeClass("nextLinkDisabled").addClass("nextLink");
					}
				}
				
				//Setup the event handlers for the prev/next links
				prevLink.bind("click", function() {

					if ($(this).hasClass("prevLinkDisabled") || $(this).hasClass("locked")) return false; //Don't do anything if this button is disabled or locked.

					//Lock buttons while scrolling
					$(this).addClass("locked");
					nextLink.addClass("locked");

					currentLeft = getCurrentLeftPos();				
					
					var newLeft = currentLeft + (itemWidth * options.scroll);
					
					//Left position should never be greater than 0
					if (newLeft > 0) {
						newLeft = 0;
					}
					
					carouselList.animate({
						left: newLeft+"px"
					}, options.speed, setButtons);
					
					$(this).trigger("blur");
					
					return false;
				
				});

				nextLink.bind("click", function() {
				
					if ($(this).hasClass("nextLinkDisabled") || $(this).hasClass("locked")) return false; //Don't do anything if this button is disabled or locked.
					
					//Lock buttons while scrolling
					$(this).addClass("locked");
					prevLink.addClass("locked");

					currentLeft = getCurrentLeftPos();				
					
					var newLeft = currentLeft - (itemWidth * options.scroll);
					
					//If there aren't enough items left to scroll a full set, just scroll the remaining items into view.					
					if ((Math.abs(newLeft) + clipWidth) > totalWidth) {						
						newLeft = (totalWidth - clipWidth) * -1; //multiply by -1 to get a negative value
					}

					carouselList.animate({
						left: newLeft+"px"
					}, options.speed, setButtons);
					
					$(this).trigger("blur");
					
					return false;
				
				});
				
				//Set the initial button state
				setButtons();

			});
		},
		
		
		/*
		* showFormErrors
		*
		* function to display any form errors that may have occured
		*
		* @param scriptOptions: Object, defines options that will override the defaults
		*
		*/
		showFormErrors: function(scriptOptions) {
			
			var defaults = {
				showInline:     true,  //boolean: set this value to false if the form errors should NOT appear after each field
				showSummary:    true,  //boolean: set this value to false if you don't want to display an error summary at the beginning of the form
				summaryAnchor:  null,  //jQuery object: the object to append the error summary to
				errorData:      null,  //object: the error data, includes all the form fields that have errors and their error messages
				scrollToSummary: true  //boolean: set this value to false if you don't want the page to auto-scroll to the error summary
			};
			
			var options = $.extend(defaults, scriptOptions);
			
			return this.each(function() {
			
				var currentForm = $(this);
				
				if (options.showSummary) {
					var errorSummary = $("<ul class=\"formErrors\"></ul>");
				}
			
				//loop through the error data
				for (field in options.errorData) {
					var currentField = currentForm.find("[name='"+options.errorData[field].name+"']");
					var currentFieldValue = currentField.val();
					var currentErrorMessage = options.errorData[field].errorMessage;
					
					currentField.addClass("error");
					
					//Add the error to the error summary (if error summary is wanted)
					if (options.showSummary) {
						var itemLabelId = currentForm.find("label[for='"+currentField.attr("id")+"']").attr("id");
						$("<li><a href=\"#"+itemLabelId+"\">"+Sprint.content.formFieldErrors.errorLabel[Sprint.currentLanguage]+" "+currentErrorMessage+"</a></li>").appendTo(errorSummary);
						
					}
					
					//Add inline error message if it's needed
					if (options.showInline) {
						$("<label for=\""+currentField.attr("id")+"\" class=\"error\">"+currentErrorMessage+"</label>").insertAfter(currentField);
					}
				}
				
				if (options.showSummary && options.summaryAnchor != undefined) {
					if (options.scrollToSummary) {
						options.summaryAnchor.scrollTo({speed: "slow"});
					}
					options.summaryAnchor.append(errorSummary);
				}
			
			});
		
		},
		
		
		/*
		* setupComponents
		*
		* function to setup various interactive components on a page, or inside a layer (ie, a modal)
		*
		* @param scriptOptions: Object, defines options that will override the defaults
		*
		*/
		setupComponents: function(scriptOptions) {
		
			var defaults = {
				buttons:               true,   //boolean: set this value to false if you don't want to setup high-res Buttons
				tooltips:              true,   //boolean: set this value to false if you don't want to setup Tooltips
				modals:                false,  //boolean: set this value to true to setup Modal launchers
				shades:                true,   //boolean: set this value to false if you don't want to setup Shades
				accordions:            true,   //boolean: set this value to false if you don't want to setup Accordions
				horizontalAccordions:  true,   //boolean: set this value to false if you don't want to setup Horizontal Accordions
				tabbedContent:         true,   //boolean: set this value to false if you don't want to setup Tabbed content layers
				disclosures:           true,   //boolean: set this value to false if you don't want to setup Disclosures
				roundedCorners:        true,   //boolean: set this value to false if you don't want to setup Rounded Corners for certain layers
				dropShadows:           true,   //boolean: set this value to false if you don't want to setup Drop Shadows for certain layers
				sifrHeadings:          true,   //boolean: set this value to false if you don't want to setup sIFR headings
				starRaters:            true    //boolean: set this value to false if you don't want to setup 5-star Rating Systems
			};
			
			var options = $.extend(defaults, scriptOptions);
			
			return this.each(function() {
			
				var baseObject = $(this);
				
				/* Convert low-res buttons to high-res buttons
				----------------------------------------------------*/
				if (options.buttons) {
					baseObject.find("input[type='submit'], input[type='button'], a.button1, a.button2, a.button3, a.button4, a.flyout").createHighResButtons();					
				}

				/* Setup Tooltips
				----------------------------------------------------*/
				if (options.tooltips) {
					//Find all the tooltip/footnote anchors and set up their event handlers to show the tooltip content
					baseObject.find(".footnoteAnchor, .tooltipAnchor").tooltip();
					baseObject.find("#footnotes, .footnotes").hide();
				}

				/* Setup Modal Launchers & Hide Modal Content
				----------------------------------------------------*/
				if (options.modals) {
					baseObject.find(".modal").hide();
					baseObject.find(".launchModal").bind("click", function() {
					
						//Find the anchor target that this links to
						var anchorTarget = $(this).attr("href");
						
						//strip off everything before the "#"
						anchorTarget = anchorTarget.substr(anchorTarget.indexOf("#"));
						
						$(anchorTarget).openModal();
						return false;
					});
				}
			
			
			
				/* Setup Help Shades
				----------------------------------------------------*/
				if (options.shades) {
					baseObject.find(".shade").shade().prev().addClass("hasShade");
				}
			
			
			
				/* Setup Accordions
				----------------------------------------------------*/
				if (options.accordions) {
					baseObject.find(".accordion").accordion();
				}
			
			
			
				/* Setup Horizontal Accordions
				----------------------------------------------------*/
				if (options.horizontalAccordions) {
					baseObject.find(".comparisonList").each(function() {
						$(this).horizontalAccordion();
					});
				}
			
			
			
				/* Setup Tabbed Content components
				----------------------------------------------------*/
				if (options.tabbedContent) {
					baseObject.find(".tabWrapper").tabbedContent({
						indexClass: "tabs"
					});
				}
			
			
			
				/* Setup Disclosure components
				----------------------------------------------------*/
				if (options.disclosures) {
					baseObject.find(".disclosure").disclosure();
				}
			
			
			
				/* Add rounded corners to certain elements
				----------------------------------------------------*/
				if (options.roundedCorners) {
					baseObject.find(".moduleDefault, .moduleFeature, .moduleFeatureDk, .accordion").addRoundedCorners();
				}
			
			
			
				/* Add dropshadows to certain elements
				----------------------------------------------------*/
				if (options.dropShadows) {
					if (!($.browser.msie && $.browser.version < 7)) {
						baseObject.find(".moduleTabbed, .moduleDefault").addDropShadows();
					}
				}
			
			
			
				/* Apply the Sprint font to certain headings using sIFR
				----------------------------------------------------*/
				if (options.sifrHeadings) {
					var baseURL = "";
		
					baseObject.find("h1.corporate, h2.corporate, h3.corporate, label.corporate").sifr({
						path: baseURL+"/global/",
						font: "sifr",
						textAlign: "left"
					});
				}



				/* 5-star Rating systems
				----------------------------------------------------*/
				if (options.starRaters) {
					baseObject.find("fieldset.rating").starRating();
				}
			
			});
		
		}
		
	});

})(jQuery);