//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,
	
	lastHttpRequest: null,
	pagination: {},
	
	nav: {},
	modal: {
		elem: {},
		dimensions: {}
	},
	
	tooltip: {
		elem: {}
	},
	
	shade: {},
	
	ieShims: {
		currentShims: 0,
		elem: new Array()
	},
	
	flashComm: {

		trackVideoPlay:function(args) {
			var videoTitle;
			videoTitle=getVideoFileDetails(args);
			Analytics.Support.trackVideoPlay(videoTitle);
		},

		trackVideoProgress25:function(args) {
			var videoTitle;
			videoTitle=getVideoFileDetails(args);
			Analytics.Support.trackVideoProgress25(videoTitle);
		},

		trackVideoProgressComplete:function(args) {
			var videoTitle;
			videoTitle=getVideoFileDetails(args);
			Analytics.Support.trackVideoProgressComplete(videoTitle);
		},

		Type:function(args) {
			return !!arg && Object.prototype.toString.call(arg).match(/(\w+)\]/)[1];
		},	

		Flash:function(id) {
			var ie = navigator.appName.indexOf("Microsoft") != -1;
			return (ie) ? window[id] : document[id];
		}

	},
	
	formFieldTypes: {
		/*
			Username
			- (1) 6-33 characters (alphabets, numbers, special characters)
			- (2) No Whitespace at the end or the beginning of the username
			- (3) Allow phone number
			- (4) Allow eMail address
			- (5) Do not allow characters(including the special characters) to be repeated three times one after the other (e.g, helllo - three 'l's not allowed one after the other)
			- (6) Allow special characters: periods, hyphens, underscores
			
			- expects the field to be passed in as a jQuery object
		*/
		username: function(field) {
		
			var validUserName = true;
			
			var fieldValue = field.val();
		
			var baseRule = /^[a-zA-Z0-9\.\-_]{1}[a-zA-Z0-9\.\-_]{4,31}[a-zA-Z0-9\.\-_]{1}$/; //Covers requirements 1, 2, 3 (sort of), and 6
			
			//First, test to see if the field value passes the base ruleset OR if it passes the email address test (covers requirements 1, 2, 3, 4, 6)
			if (baseRule.test(fieldValue) || Sprint.formFieldTypes.emailAddress.test(fieldValue)) {
				
				//Field passes one of the tests, check for repeating characters
				var numCharacters = fieldValue.length;
				
				var prevChar = "";
				var sameCharCount = 0;
				
				//Loop through the characters 1 by 1
				for (var i = 0; i < numCharacters; i++) {
					
					var currentChar = fieldValue.substr(i, 1);

					//Check this character to see if it's the same as the last...
					if (currentChar == prevChar) {
						sameCharCount++;

						//This covers requirement #5
						if (sameCharCount >= 2) {
							validUserName = false;
							break;
						}
						
					}
					else {
						sameCharCount = 0;
					}

					prevChar = currentChar;
					
				}
				
			}
			else {
				validUserName = false;
			}			
			
			return validUserName;
		
		},
		
		/*
			Display Name
			- Must start with a letter or number: [a-zA-Z0-9]{1}
			- Must be followed by followed by 5-49 of these characters: a-z, A-Z, 0-9, _, -, ., \s(spaces): [a-zA-Z0-9_\.\-]{5,32}
				- ensures that display names will be 6-50 chars
		*/
		displayName: /^[a-zA-Z0-9]{1}[a-zA-Z0-9_\.\-\s]{5,49}$/,
		
		/*
			Password
			- (1) 6-33 characters (alphabets, numbers, special characters)
			- (2) Do not allow characters(including the special characters) to be repeated three times one after the other (e.g, helllo - three 'l's not allowed one after the other)
			- (3) No spaces
			- (4) Allow special characters: periods, hyphens and underscore

			- expects the field to be passed in as a jQuery object
		*/
		password: function(field) {

			var validPassword = true;
			
			var fieldValue = field.val();
		
			var baseRule = /^[a-zA-Z0-9\.\-_]{6,33}$/; //Covers requirements 1, 3, and 4

			//First, test to see if the field value passes the base ruleset (covers requirements 1, 3, 4)
			if (baseRule.test(fieldValue)) {
				
				//Field passes one of the tests, check for repeating characters
				var numCharacters = fieldValue.length;
				
				var prevChar = "";
				var sameCharCount = 0;
				
				//Loop through the characters 1 by 1
				for (var i = 0; i < numCharacters; i++) {
					
					var currentChar = fieldValue.substr(i, 1);

					//Check this character to see if it's the same as the last...
					if (currentChar == prevChar) {
						sameCharCount++;

						//This covers requirement #2
						if (sameCharCount >= 2) {
							validPassword = false;
							break;
						}
						
					}
					else {
						sameCharCount = 0;
					}

					prevChar = currentChar;
					
				}
				
			}
			else {
				validPassword = false;
			}			
			
			return validPassword;

		},
		
		/*
			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-6 of the following characters a-z, A-Z: [A-Za-z]{2,6}
		*/
		emailAddress: /^[a-zA-Z0-9_\.\-]+\@([a-zA-Z0-9\-]+\.)+[A-Za-z]{2,6}$/,
	
		/*
			Phone Number
			- Optional bracket: \(?
			- 3 digits for area code: \d{3}
			- Optional bracket: \)?
			- Optional Space, Period or Dash [\s|\-|\.]?
			- 3 digits: \d{3}
			- Optional Space, Period 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;
		},
		
		/*
			Select
			- returns true if any option is selected, as long as its value is not "-"
			- expects the field to be passed in as a jQuery object
		*/
		select: function(field) {
			if (field.val() != "-") {
				return true;
			}
			return false;
		},
		
		/*
			ESN
			- 11 digits: \d{11} OR
			- 8 Hex Digits [a-fA-F0-9]{8}
		*/		
		ESN: /^\d{11}$|^[a-fA-F0-9]{8}$/,
		
		/*
			MEID
			- 18 digits: \d{18} OR
			- 14 Hex Digits [a-fA-F0-9]{14}
		*/		
		MEID: /^\d{18}$|^[a-fA-F0-9]{14}$/,
		
		/*
			BAN
			- 9 digits: \d{9}
		*/		
		BAN: /^\d{9}$/,
		
		/*
			DAC
			- 9 digit BAN prefix: \d{9}
			- required dash seperator: \-
			- 4 - 10 digits: \d{4,10}
		*/		
		DAC: /^\d{9}\-\d{4,10}$/,	
		/*
			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 (0-9), but also accepts the letters B/b, I/i, L/l, O/o and S/s in place of the numbers 8, 1, 1, 0 and 5: [0-9bilosBILOS]{8}
		*/
		validationCode: /^[0-9bilosBILOS]{8}$/,
		
		/*
			Business User Passcode
			- Any 1-20 characters: .{1,20}
		*/
		passcode: /^.{1,20}$/,
		
		/*
			Security Hint Answer
			- Any 2-30 characters: .{2,30}
		*/
		securityHintAnswer: /^.{2,30}$/,
		
		/*
			First Name
			- 2-32 of these characters: a-z, A-Z, 0-9, _, -, .: [a-zA-Z0-9_\.\-]{2,32}
		*/
		firstName: /^[a-zA-Z0-9_\.\-]{2,32}$/,
		
		/*
			Initial
			- 1-32 of these characters: a-z, A-Z: [a-zA-Z]{1,32}
		*/
		initial: /^[a-zA-Z0-9_\.\-]{1,32}$/,
		
		/*
			Last Name
			- 2-60 of these characters: a-z, A-Z, 0-9, _, -, .: [a-zA-Z0-9_\.\-]{2,60}
		*/
		lastName: /^[a-zA-Z0-9_\.\-]{2,60}$/,
		
		/*
			Phone Extension Number
			- 1-4 digits: \d{1,4}
		*/
		phoneExtension: /^\d{1,4}$/,
		
		/*
			Address Order Number
			- Any 2-25 characters: .{2,25}
		*/
		addressOrderNumber: /^.{2,25}$/,
		
		/*
			Street Address
			- Any 2-66 characters: .{2,66}
		*/
		streetAddress: /^.{2,66}$/,
		
		/*
			Generic Text Field (TBD)
			- Any 2-36 characters: .{2,36}
		*/
		text: /^.{2,36}$/,
		
		/*
			City
			- Any 2-26 characters: .{2,26}
		*/
		city: /^.{2,26}$/,
		
		/*
			ZIP Code
			- 5 digits: \d{5}
		*/
		zipCode: /^\d{5}$/,
		
		/*
			Tax ID Number (TBD)
			- 2 digits: \d{2}
			- Optional Space, Period or Dash [\s|\-|\.]?
			- 4 digits: \d{4}
		*/
		taxId: /^\d{2}[\s|\-|\.]?\d{4}$/,
		
		/*
			Voicemail Passcode
			- 4-10 digits
		*/
		voicemailPasscode: /^\d{4,10}$/
		
	},
	
	//Helper functions
	fn: {
		/*
		* ajaxError
		*
		* general purpose function to display a standard error when ajax functions go bad
		*
		* @param event: the event being generated by an ajax call
		*
		*/
		ajaxError: function(event) {
			alert(Sprint.content.ajax.error[Sprint.currentLanguage] + event.status);
		},
		
		/*
		* 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+"']");
				
				//Make sure the field actually exists before trying to validate it (this supports forms that have different fields in different scenarios.
				if (currentFieldObj.length > 0) {
					var currentFieldValue = currentFieldObj.val();
					
					//If the field is a checkbox and it's not required, we need to re-set the currentFieldValue variable, as checkboxes will always return a value of "on" regardless.
					if (!currentField.required && currentFieldObj.is(":checkbox")) {
						currentFieldValue = "";
					}
	
					//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 the field isn't required, and it's a select field with a value of "-", break out of the if statement
						if (!currentField.required && currentField.type == "select" && currentFieldValue == "-") break;
						
						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;
		},
		
		/*
		* deserialize
		*
		* function to de-serialize a serialzed string and convert it to an object
		*
		* @returns object
		*/
		deserialize: function(serializedString) {
			var deserializedObject = {};
			
			var parts = serializedString.split("&");
			
			var numParts = parts.length;
			
			for (i = 0; i < numParts; i++) {
				var currentPair = parts[i].split("=");
				
				deserializedObject[currentPair[0]] = currentPair[1];
			}
			
			return deserializedObject;
		},
		
		/*
		* cloneObject
		*
		* function to clone an object
		*
		* @returns object
		*/
		cloneObject: function(obj) {
			if (obj == null || typeof(obj) != "object") {
				return obj;
			}

			var temp = new obj.constructor();

			for (var key in obj) {
				temp[key] = Sprint.fn.cloneObject(obj[key]);
			}

			return temp;
		},
		
		/*
		* getQueryString
		*
		* function to get a URL querystring
		*
		* @param urlString: a URL that contains a querystring
		*
		* @returns the querystring as a String
		*/
		getQueryString: function(urlString) {
		
			//Strip off everything before the question mark
			querystring = urlString.substr(urlString.indexOf("?"));
			
			return querystring;
		},
		
		/*
		* getURLParameter
		*
		* function to get the value of a variable in a URL querystring
		*
		* @param querystring: the Query String of a URL, starting with a question mark (eg: ?variable1=value&variable2=value)
		* @param variable: the variable to get the value of
		* @param doNotDecode: boolean flag (default value is false) that determines whether or not to decode the parameter (URL decoding)
		*
		* @returns the value of the desired variable
		*/
		getURLParameter: function(querystring, variable, doNotDecode) {
		
			//Remove the question mark
			querystring = querystring.substr(1);
		
			var variables = querystring.split("&");

			var numberOfVariables = variables.length;

			for (var i = 0; i < numberOfVariables; i++) {
				
				var currentPair = variables[i].split("=");
				
				if (currentPair[0] == variable) {
					return (doNotDecode)?currentPair[1]:decodeURIComponent(currentPair[1]);
				}
			}
			
			return undefined; //If no variable of the requested name is found
		},
		
		/*
		* isItemInView
		*
		* function to determine whether or not an item is visible in the browser's viewport
		*
		* @param object: the object we're looking for, a jQuery object
		*
		* @returns boolean: true if fully in view (vertically), false if not
		*/
		isItemInView: function(object) {
			var $ = jQuery;

			var currentScrollTop = 0;
			
			var windowHeight = $(window).height();
			
			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 (currentScrollTop + windowHeight < object.outerHeight() + object.offset().top) {
				return false;
			}
			
			return true;
		}
	}
	
};


function getVideoFileDetails(args) {
				var title="";
				if(	Analytics.MetricData.articleName == undefined)
			{
				for (property in args) {  title += property + ': ' + args[property]+'; ';}
				var startIndex=title.lastIndexOf("/");
				if(startIndex != -1 )
				{
					title=title.substring(startIndex+1);
					endIndex=title.lastIndexOf(".");
					if(endIndex != -1 )
					{
						title=title.substring(0,endIndex);
					}
				}
			}
			else
			{
               title=Analytics.MetricData.articleName;
			}
			return title;
		}

//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:  false,   // boolean: set this value to true to allow clicking on the overlay to close 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(749); // 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=\"/global/images/template/widgets/modal/ico_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
					//$("body *").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();
							}
							
							if (!options.ajaxContent) {
								//Set focus to the first focusable element in the modal (this will be done separateley for ajax modal content)
								Sprint.modal.elem.focusFirstElement({
									focusableElements: "input[type!='hidden'], select, textarea, button, a[href]",
									containFocus: true
								});
							}
							
						}, 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();

				//Remove the modal overlay (and the SHIM behind it)
				$("#modalOverlay").removeShim().hide().remove();

				//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
			});

			//Reset the dimensions for the overlay now that the modal dimensions have been determined. (after a very fast 1 milisecond delay)
			setTimeout(function() {
				var modalOverlay = $("#modalOverlay");
				modalOverlay.css({width: 0, height: 0}); //Start by resetting overlay width/height to 0
				
				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;
	
				dimensions = Sprint.modal.dimensions;
	
				modalOverlay.css({
					width:	 dimensions.overlayWidth,
					height:  dimensions.overlayHeight
				}).removeShim().addShim();
			}, 1);

			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) {
			
			//Show the loading spinner while we load in the modal content via ajax.
			var spinnerHTML = "<div id=\"modalSpinner\">"+Sprint.content.modal.loadingText[Sprint.currentLanguage]+"</div>";
			
			//Wait for a half second and if the modal content hasn't loaded in by then, show the loading spinner so the user knows something is happening.
			var spinnerTimer = setTimeout(function() {
				modal.empty().html(spinnerHTML);
				Sprint.modal.elem.sizeModal();
			}, 500);
			
			$.ajax({
					
				data: "ajax=true",
				type: "GET",
				url: ajaxPath,
				cache: false, //Do not allow the browser to cache these pages
				async: false, //Make this ajax call finish before executing other functions
				
				success: function(data) {
					//Comment out these lines to see progress indicator
					modal.empty().html(data);
					clearTimeout(spinnerTimer);
				},
				
				error: Sprint.fn.ajaxError

			});

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

			//Resize the modal based on the new content
			Sprint.modal.elem.sizeModal();
			
			
			Sprint.modal.elem.focusFirstElement({
				focusableElements: "input[type!='hidden'], select, textarea, button, a[href]",
				containFocus: true
			});
			


		},

		/*
		* 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
				belowAnchor:    false,  // boolean: set this value to true if the tooltip should appear below the anchor
				overrideClass:  "",      // string: CSS class that will be added to the tooltip when it's shown
				disableClick: true		// boolean: set this value to false if this tooltip will have a click function
			};

			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").append(tooltip);
			}

			return this.each(function() {
				
				// determine the element that the tooltip will be anchored (positioned) to.
				var anchor = $(this);

				if(options.disableClick){
					// 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")) {
							
							var field = $("#"+$(this).parent().attr("for"))
							
							//Fire the click event if it's a radio or checkbox to make sure it gets checked.
							if (field.is(":radio") || field.is(":checkbox")) {
								field.click().trigger("click");
							}
							else {
								field.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
						}
						else {
							//If not, just reset the tooltip class
							tooltip.attr("class", "tooltipChrome");
						}
						
						
						//Show the tooltip after a 1/4 second delay
						Sprint.tooltip.showTimer = setTimeout(function() {
						
							//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 below the anchor
							if (options.belowAnchor) {
								tooltip.addClass("tooltipBelow");
							}
							else {
								tooltip.removeClass("tooltipBelow");
							}
							
							//Add Override Class
							if (options.overrideClass) {
								tooltip.addClass(options.overrideClass);
							}
							
							// 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 - 3; //Subtract a few pixels to give the tooltip some space
							
							if (options.belowAnchor) {
								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
							
							//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() {
							
								tooltip.addShim(parseInt(tooltip.css("zIndex") - 1));
							
								Sprint.tooltip.tooltipScroll = setTimeout(function() {
	
									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 it's not a "below-anchor" tooltip, and the tooltip isn't fully visible in the current viewport, scroll to the top of the tooltip.
									if (!options.belowAnchor) {
										
										//If the top of the tooltip isn't already in view, scroll to it.
										if (tooltip.offset().top < currentScrollTop) {
											tooltip.scrollTo({speed: "slow"});
										}
									}
									else {
										//Check to see if the tooltip is fully visible in the current viewport, if not, scroll the page so the tooltip is fully visible.
										if (currentScrollTop + windowHeight < tooltipHeight + tooltip.offset().top) {
											tooltip.scrollTo({
												speed: "slow",
												topOffset: windowHeight - tooltipHeight - 15
											});
										}
									}
								}, 100); //Wait a little bit before checking the scroll position
							}
		
							//Finally, 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");
						}, 250); //Show after 1/4 second delay
	
						//bind the mouseout event to hide the tooltip
						anchor.bind("mouseout", function() {
						
							//Clear the show timeout (if it's running)
							clearTimeout(Sprint.tooltip.showTimer);
						
							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();
						//Remove the override class if the tooltip has it
						tooltip.removeClass(options.overrideClass);
					
						//Reset tooltip class
						tooltip.attr("class", "tooltipChrome");
					}
					else {
						tooltip.fadeOut("normal", function() {
							//Remove the override class if the tooltip has it
							tooltip.removeClass(options.overrideClass);
					
							//Reset tooltip class
							tooltip.attr("class", "tooltipChrome");
						});
					}
					
					tooltip.removeShim();
					
					Sprint.tooltip.visible = false;
					$("a.currentTooltip").removeClass("currentTooltip").unbind("mouseout");
					tooltip.unbind("mouseover");
					tooltip.unbind("mouseout");
					
					clearTimeout(Sprint.tooltip.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.removeShim();
					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);
						shade.addShim();
					});
				}
			};
		},

		/*
		* 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 leftSide 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 after each tab switch
				useFullTitleContents:  false           // boolean: set this value to true to use the full contents of the sectionTitle instead of just the text.
			};

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

			return this.each(function() {

				var tabbedSection = $(this);
				
				var defaultTab = 0; //Index of the tab that starts off selected
			
		
				//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;
					var newTab;
					
					if (options.useFullTitleContents) {
						tabTitle = $(this).children(".sectionTitle").html();
						newTab = $("<li>"+tabTitle+"</li>");
					}
					else {
						tabTitle = $(this).children(".sectionTitle").text();
					
						//Added to have an onclick event for "Image Tab" :Omniture Tracking
						if("Images" == tabTitle)
						{
								newTab = $("<li><a href=\"#\" onclick=\"trackImageTabClickEvent();\" >"+tabTitle+"</a></li>");
						}
						else
						{					
								newTab = $("<li><a href=\"#\">"+tabTitle+"</a></li>");
						}
					}
					
					if (tabID != "") {
						newTab.attr("id", tabID);
					}

					$(this).children(".sectionTitle").attr("id", "").hide();					
					
					newTab.appendTo(tabIndex);
					
					//If this tab is set to be selected by default, store the index value in the defaultTab variable
					if ($(this).is(".selected")) {
						defaultTab = i;					
					}
					
				});
				
				//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;
				
				//Start off with the default tab open (determined above)
				selectTab(defaultTab);
				
				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 firstSelected beforeSelected 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.removeClass("disclosureClosed").addClass("disclosureOpen");
						$(this).html(options.openedText);
					}
					else {
						//Close	it
						disclosure.find(".disclosureContent:first").slideUp(options.speed, options.closeCallback);
						disclosure.removeClass("disclosureOpen").addClass("disclosureClosed");
						$(this).html(options.closedText);
					}
					
					return false;
				});
				
				if (options.titleClickable) {
					//Let users click on the heading element to open/close the disclosure
					disclosure.find(".disclosureTitle:first").css("cursor", "pointer");
					
					disclosure.find(".disclosureTitle:first").bind("click", function() {
					
						disclosure.find(".disclosureToggle:first").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() {
			
				var currentObject = $(this)
			
				var dropShadow = $("<div class=\"dropShadow\"></div>");
				
				dropShadow.width(currentObject.outerWidth() - 4);
				
				dropShadow.appendTo(currentObject);
				
			});
			
		},
		
		/*
		* equalizeHeights
		*
		* function to equalize the heights of a set of elements
		*
		*/
		equalizeHeights: function() {
		
			var finalHeight = 0;
			
			$(this).each(function() {
				
				if ($(this).outerHeight() > finalHeight) {
					finalHeight = $(this).outerHeight();
				}
				
			});
			
			$(this).each(function() {
				$(this).height(finalHeight - ($(this).outerHeight() - $(this).height()));
			});
			
			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 button2 button3 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() {
							
								//only fire the click event if this button is enabled
								if (!newAnchor.is(".disabled")) {
									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() {
					
					// determines if the user already submitted a star rating
					// if(!currentRater.hasClass('ratingsDone')){
					
						//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
		*
		* @param zIndex: Integer: the z-index for the shim, if required
		*
		*/
		addShim: function(zIndex) {
		
			//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
						});
						
						//Add a z-index if it's passed in.
						if (zIndex && zIndex > 0) {
							newShim.css("z-index", zIndex);
						}
						
						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
				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
				type: 			"list"		  //string: determines if the content is in a "list" or a "table"
			};
			
			var options = $.extend(defaults, scriptOptions);
		
			return this.each(function() {
			
				//add base class to allow all carousels to inherit CSS styles
				$(this).addClass(options.baseClass);
				
				// if type equals "table", then carousel function searches for a table and gathers proper values, 
				// else it defaults to building a standard list carousel
				if(options.type == 'table'){
					
					var carouselList = $(this).find("table");
					
					var numberOfItems = carouselList.children("tbody").children("tr:first").find("td").length;
					
					var itemWidth = carouselList.children("tbody").children("tr:first").find("td").width();
					
					var itemOuterWidth = carouselList.children("tbody").children("tr:first").find("td").outerWidth(true); 
					
					var rightOffset = 0;
				
					//Determine the correct rightOffset amount to use to account for the list item right margins
					if (!isNaN(parseInt(carouselList.children("tbody").children("tr:first").find("td").css("marginRight")))) {
						rightOffset = parseInt(carouselList.children("tbody").children("tr:first").find("td").css("marginRight"));
					}
					
					if (itemWidth == 0) {
						//Check to see if there's a CSS width
						if (isNaN(parseInt(carouselList.children("tbody").children("tr:first").find("td").css("width")))) {
							itemWidth = 0;
						}
						else {						
							itemWidth = parseInt(carouselList.children("tbody").children("tr:first").find("td").css("width"));
							
							//check if there's any outerWidth - if so, add the value to the itemWidth
							if (itemOuterWidth > 0) {
								itemWidth = itemWidth + itemOuterWidth;
							}
						}
					}
										
					var tableHeight = carouselList.height();
				
				} else {
				
					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);
				
				//Set height propertiy to .carouselClip if the carousel contains a table.
				if(options.type == 'table'){
					$(this).find(".carouselClip").height(tableHeight).children('table').css('position', 'absolute');
				}
				
				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;
				}
				
				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 - rightOffset) * -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
				showErrorsCallback: null   //function: a function that will be run after the table gets sorted
			};
			
			var options = $.extend(defaults, scriptOptions);
			
			return this.each(function() {
			
				var currentForm = $(this);
				var numOfErrors = 0;
				
				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;
					if($("#OmnitureCalls").val() == "false"){
					//Omniture tracking the error message shown to the User.
						Analytics.Support.trackError("user",currentErrorMessage);
					}

					currentField.addClass("error");
					
					if (currentField.length < 1) break;
					numOfErrors++;
					
					//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");
						
						if (!itemLabelId) {
							//If the label doesn't have an ID to link to, use the field ID instead
							itemLabelId = currentField.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) {
					
						//Create the error message
						var inlineErrorMessage = $("<label for=\""+currentField.attr("id")+"\" class=\"error\">"+currentErrorMessage+"</label>");
						
						//Check to see if this field is in a row with other fields
						if (currentField.parent().hasClass("multiFieldRow")) {
							//Check to see if the multiFieldErrors div already exists
							var multiFieldErrorDiv = currentField.parent().parent().find(".multiFieldErrors");
							
							//If it doesn't exist, create it
							if (multiFieldErrorDiv.length < 1) {
								multiFieldErrorDiv = $("<div class=\"multiFieldErrors\"></div>").appendTo(currentField.parent().parent());
							}
							
							//Create the error message and append it to the multiFieldErrorDiv
							inlineErrorMessage.appendTo(multiFieldErrorDiv);
						}
						//Check to see if the current field is a radio button (and only show 1 error message)
						else if (currentField.attr("type").toLowerCase() == "radio") {
							var errorTargetLayer;
							
							//If the field is inside a label
							if (currentField.parent("label").length > 0) {
								errorTargetLayer = currentField.eq(0).parent().parent();
							}
							else {
								errorTargetLayer = currentField.eq(0).parent();
							}
							
							inlineErrorMessage.addClass("radioGroupError").insertBefore(errorTargetLayer);
						}
						//Check to see if the current field is inside a label
						else if (currentField.parent("label").length > 0) {
							inlineErrorMessage.appendTo(currentField.parent().parent());
						}
						else {
							inlineErrorMessage.insertAfter(currentField);
						}
					}
				}
				
				if (options.showSummary && options.summaryAnchor != undefined && numOfErrors > 0) {
					if (options.scrollToSummary) {
						options.summaryAnchor.scrollTo({speed: "slow"});
					}
					options.summaryAnchor.append(errorSummary);
				}
				
				//If there's a callback function to be run after showing errors, run it now
				if (options.callback) {
					options.callback();
				}
			
			});
		
		},



		/*
		* focusFirstElement
		*
		* function to set focus to the first focusable element in a layer (most-often used with Modals)
		*
		* @param scriptOptions: Object, defines options that will override the defaults
		*
		*/
		focusFirstElement: function(scriptOptions) {
		
			var defaults = {
				focusableElements:  "input[type!='hidden'], select, textarea, button, a[href]",  //string: jQuery selector string used to find focusable elements
				containFocus:       false  //boolean: set this value to true if you want to contain focus to this layer	(TBD)			
			};
			
			var options = $.extend(defaults, scriptOptions);
			
			var baseElement = $(this);
			var focusableElements = baseElement.find(options.focusableElements);
			
			//exit the function if there are no focusable elements
			if (focusableElements.length < 1) {
				return this;
			}
			
			/*
			if (options.containFocus) {
				
				var lastFocusedItem;
			
				focusableElements.bind("focus", function() {				
					$(this).addClass("hasFocus");
					lastFocusedItem = $(this);					
				}).bind("blur", function() {
					$(this).removeClass("hasFocus");
				});

				var shiftKeyDown = false; //For backwards tabbing
				
				function tabIntercept(event) {
				
					//Check to see if the focusable elements are still in the <body>, if not, get rid of the assigned functions.
					if (focusableElements.eq(0).parents("body").length < 1) {
						$(window).unbind("keydown", tabIntercept);
						$(window).unbind("keyup", checkShiftKey);
						return;
					}
					
					if (event.keyCode == 16) {
						shiftKeyDown = true;
					}
					
					if (event.keyCode == 9) {
						event.preventDefault();
						
						var nextItem;
						
						if (shiftKeyDown) {
							//If the shift key is down, go to the previous item
							if (lastFocusedItem.length > 0 && focusableElements.index(lastFocusedItem) != 0) {
								nextItem = focusableElements.eq(focusableElements.index(lastFocusedItem) - 1);
							}
							else {
								nextItem = focusableElements.eq(focusableElements.length - 1);
							}
						}
						else {
							//Go to the next item, like normal
							if (lastFocusedItem.length > 0 && focusableElements.index(lastFocusedItem) < focusableElements.length - 1) {
								nextItem = focusableElements.eq(focusableElements.index(lastFocusedItem) + 1);
							}
							else {
								nextItem = focusableElements.eq(0);
							}
						}
						
						lastFocusedItem.trigger("blur");
						nextItem.focus();
					}
				}
				
				function checkShiftKey(event) {
					//On key up, if it's the shift key, reset the shiftKeyDown variable
					if (event.keyCode == 16) {
						shiftKeyDown = false;
					}
				}
			
				$(window).bind("keydown", tabIntercept);
				$(window).bind("keyup", checkShiftKey);
			}
			*/
			
			setTimeout(function() {
				focusableElements.eq(0).focus();
			}, 1);
			
			return this;
		},


		
		/*
		* sortableTable
		*
		* function to turn tables into sortable tables by clicking on their table column headers
		*
		* @param scriptOptions: Object, defines options that will override the defaults
		*
		*/
		sortableTable: function(scriptOptions) {
		
			var defaults = {
				reverseSort:           false,  //boolean: set this value to true if you want a reverse-sort table (to sort both ascending and descending) - TO DO!
				pagination:            false,  //boolean: set this value to true if you'd like pagination for this table
				paginationAbove:       true,   //boolean: set this value to false if you don't want the pagination controls to appear above the table
				paginationAboveAnchor: null,   //jQuery object: the anchor to which the pagination controls will be appended to (will be inserted directly above the table if this isn't set)
				paginationBelow:       true,   //boolean: set this value to false if you don't want the pagination controls to appear below the table
				paginationBelowAnchor: null,   //jQuery object: the anchor to which the pagination controls will be appended to (will be inserted directly below the table if this isn't set)
				paginationNumbers:     true,   //boolean: set this value to false if you don't want numbers in your pagination controls
				paginationNextPrev:    true,   //boolean: set this value to false if you don't want the Next/Previous buttons to show in your pagination controls
				rowsPerPage:           15,     //integer: the number of rows in the table to show for each page (if pagination is requested)
				customSortFunctions:   null,   //array of functions: tied to the column index
				excludeColumns:        null,   //array: array of the column indexes to exclude from having sort functionality
				columnValueFunctions:  null,   //array of functions: custom functions to get the value for a column, ordered by index. SYNTAX: function(cell) { return "value" } where cell is the TD (as a jQuery object) and value is the String returned - if there's no function for a given column, the fallback will be jQuery's .text() function.
				beforeSort:            null,   //function: a function that will be run before the table gets sorted
				afterSort:             null,    //function: a function that will be run after the table gets sorted
				afterPageUpdate:       null    //function: a function that will be run after the page gets updated
			};
			
			var options = $.extend(defaults, scriptOptions);
			
			return this.each(function() {
			
				var currentTable = $(this);			
				var thead = currentTable.find("thead");
				
				function inArray(targetArray, searchValue) {

					var inArray = false;
					
					$.each(targetArray, function(i) {
					
						if (inArray) {
							return;
						}
					
						if (targetArray[i] == searchValue) {
							inArray = true;
						}
					});
					
					return inArray;
				}
				
				function switchToPage(pageNumber) {
					
					if (pageNumber < 1) {
						return false;
					}
					
					var startRow = ((pageNumber - 1) * options.rowsPerPage) + 1;
					
					if (pageNumber == 1) {
						startRow = 1;
					}

					var endRow = startRow + options.rowsPerPage;
					var rows = currentTable.find("tbody tr");
					var totalPages = Math.ceil(rows.length / options.rowsPerPage);
					
					rows.each(function(i) {
					
						if ((i + 1 >= startRow) && (i + 1 < endRow)) {
							$(this).show();						
						}
						else {
							$(this).hide();
						}
					
					});
					
					//Store the current page...
					currentTable.data("currentPage", pageNumber);
					
					if (options.pagination) {
					
						var paginationLinks = $("div.tablePagination a");
						
						if (paginationLinks.length > 0) {
						
							paginationLinks.each(function() {
							
								var currentLink = $(this);
								
								//Look at the href value
								var href = currentLink.attr("href");
								
								href = href.substr(href.indexOf("?"));
								href = Sprint.fn.getURLParameter(href, "p");

								if (href == pageNumber && !currentLink.is(".prev") && !currentLink.is(".next")) {
									currentLink.addClass("selected");
								}
								else {
									currentLink.removeClass("selected");
								}
								
								if (currentLink.is(".prev")) {
									if (pageNumber > 1) {
										currentLink.attr("href", "?p="+(parseInt(pageNumber) - 1)).removeClass("disabled");
									}
									else {
										currentLink.attr("href", "?p=0").addClass("disabled");
									}
								}
								else if (currentLink.is(".next")) {
									if (pageNumber < totalPages) {
										currentLink.attr("href", "?p="+(parseInt(pageNumber) + 1)).removeClass("disabled");
									}
									else {
										currentLink.attr("href", "?p=0").addClass("disabled");
									}
								}
							});
						
							//IF there's a callback function to be run after the page gets updated run it.
							if (options.afterPageUpdate) {
								options.afterPageUpdate();
							}
						}
					}
				}
				
				//Wrap all thead links in anchor tags
				thead.find("th").each(function(i) {
					
					var exclude = false;					
					
					//Figure out if this column should be included...
					if (options.excludeColumns) {						
					
						if (inArray(options.excludeColumns, i)) {
							exclude = true;
						}
					
					}
					
					if (!exclude) {
					
						//Wrap the TH text in an anchor (if there isn't already an anchor in the heading)
						var currentTH = $(this);
						
						if (currentTH.find("a").length < 1) {
							currentTH.html("<a href=\"#\">"+currentTH.html()+"</a>");
						}
						else {
							currentTH.find("a").unbind("click"); //If there's already an anchor tag, unbind it's current click event.
						}
					}
					
				});

				function setupPagination() {
					//Remove existing pagination controls (if there are any)
					if (currentTable.data("paginationAbove")) {
						currentTable.data("paginationAbove").remove();
					}
					if (currentTable.data("paginationBelow")) {
						currentTable.data("paginationBelow").remove();
					}
					
					//Determine the total number of pages required.
					var resultsPerPage = options.rowsPerPage;
					var totalPages = Math.ceil(currentTable.find("tbody tr").length / resultsPerPage);
					
					//Store the total pages
					currentTable.data("totalPages", totalPages);

					if (totalPages > 1) {
					
						//Create pagination controls
						var paginationControls = $("<ul class=\"pageList\"></ul>");
						
						//for each page, create a numbered link (if required)
						if (options.paginationNumbers) {
							for (i = 1; i <= totalPages; i++) {
								if (i == 1) {
									paginationControls.append("<li class=\"first\"><a href=\"?p="+i+"\">"+i+"</a>");
								}
								else if (i == totalPages) {
									paginationControls.append("<li class=\"last\"><a href=\"?p="+i+"\">"+i+"</a>");
								}
								else {
									paginationControls.append("<li><a href=\"?p="+i+"\">"+i+"</a>");
								}
							}
						}
						
						if (options.paginationNextPrev) {
							paginationControls.prepend("<li class=\"pagePrev\"><a href=\"?p=0\" class=\"prev\">Previous</a></li>");
							paginationControls.append("<li class=\"pageNext\"><a href=\"?p=0\" class=\"next\">Next</a></li>");
						}
						
						//Event handling for pagination controls
						paginationControls.bind("click", function(event) {
							event.preventDefault();
							
							var anchor = $(event.target);
							
							if (anchor.is("a") && !anchor.is(".selected")) {
							
								var currentHref = anchor.attr("href");
								
								//Strip off everything before the ? character
								currentHref = currentHref.substr(currentHref.indexOf("?"));
							
								var pageToShow = Sprint.fn.getURLParameter(currentHref, "p");
								
								switchToPage(pageToShow);
								
							}
							
						});
						
						var paginationAbove;
						var paginationBelow;
						
						if (options.paginationAbove) {
							paginationAbove = $("<div class=\"tablePagination tablePaginationAbove\"></div>");
							
							paginationAbove.append(paginationControls);							
							
							if (options.paginationAboveAnchor) {
								options.paginationAboveAnchor.append(paginationAbove);
							}
							else {
								paginationAbove.insertBefore(currentTable);
							}
							
							currentTable.data("paginationAbove", paginationAbove);
						}
						
						if (options.paginationBelow) {
							paginationBelow = $("<div class=\"tablePagination tablePaginationBelow\"></div>");

							paginationBelow.append(paginationControls.clone(true))
							
							if (options.paginationBelowAnchor) {
								options.paginationBelowAnchor.append(paginationBelow);
							}
							else {
								paginationBelow.insertAfter(currentTable);
							}
							
							currentTable.data("paginationBelow", paginationBelow);
						}
					
					}
				}
				
				//Setup pagination (if required)
				if (options.pagination) {				
					setupPagination();
					currentTable.data("resetPagination", setupPagination);
					currentTable.data("switchToPage", switchToPage);
				}
				
				function sortTableByColumn(columnIndex) {
				
					var selectedTH = currentTable.find("thead th").eq(columnIndex);
				
					var tbody = currentTable.find("tbody");
					var oldRows = tbody.find("tr");
					
					var sortArray = new Array();
					var newRows = new Array();
					
					var sortDirection = "ASC";

					//Re-set sibling TH elements to ensure they don't have any sort classes applied.
					selectedTH.siblings().removeClass("sortedAsc sortedDesc");
					
					if (options.reverseSort && selectedTH.hasClass("sortedAsc")) {
						sortDirection = "DESC";
						selectedTH.removeClass("sortedAsc").addClass("sortedDesc");
					}
					else {
					
						//If the table is already sorted by this column in ASC (and reverseSort is set to false), exit this function
						if (selectedTH.hasClass("sortedAsc")) {
							return;
						}
					
						sortDirection = "ASC";
						selectedTH.removeClass("sortedDesc").addClass("sortedAsc");
					}
					
					//If there's a callback function to be run before sorting the table, run it now
					if (options.beforeSort) {
						options.beforeSort();
					}
					
					oldRows.each(function(i) {
					
						var currentTR = $(this);
					
						var currentCellValue = currentTR.find("td").eq(columnIndex).text();
						
						//Check to see if this column has a custom function to get the value
						if (options.columnValueFunctions && options.columnValueFunctions[columnIndex]) {
							currentCellValue = options.columnValueFunctions[columnIndex](currentTR.find("td").eq(columnIndex));
						}
					
						//trim white-space and convert all strings to lowercase to perform case insensitive sorting.
						currentCellValue = $.trim(String(currentCellValue).toLocaleLowerCase());
					
						//Store this column's value in an array along with the row itself
						sortArray[i] = [currentCellValue, currentTR];
						
					});
					
					//Determine whether or not to use a custom sort function, or the default array sort function.
					if (options.customSortFunctions && options.customSortFunctions[columnIndex]) {
						sortArray.sort(options.customSortFunctions[columnIndex]);
					}
					else {
						sortArray.sort();
					}
					
					//re-order the rows based on their new order in the sortArray
					$.each(sortArray, function(i) {

						//Sort the table (Ascending, or Descending)
						if (sortDirection == "DESC") {
							tbody.prepend(sortArray[i][1]);
						}
						else {
							tbody.append(sortArray[i][1]);
						}
						
					});
					
					//Go back to page 1 (if there's pagination & more than 1 page)
					if (options.pagination && Math.ceil(currentTable.find("tbody tr").length / options.rowsPerPage) > 1) {
						switchToPage(1);
					}
					
					//IF there's a callback function to be run after the table gets sorted, run it now
					if (options.afterSort) {
						options.afterSort();
					}
				
				}
				
				thead.bind("click", function(event) {
				
					var target = $(event.target)

					if (target.is("a") || target.parents("a").length > 0) {
					
						if (!target.is("a")) {
							//find the parent a tag
							target = target.parents("a");
						}

						//find the column index of this TH
						var columnIndex = thead.find("th").index(target.parent());

						sortTableByColumn(columnIndex);
					}
				
					return false;
				});
				
				//Initialize the table with the first page
				if (options.pagination) {
					switchToPage(1);
				}
			
			});
		},



		/*
		* 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
				pngFix:            	   true    //boolean: set this value to false if you don't want to setup the pngFix
			};
			
			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. .calloutBanner").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 = "";
		
					//Make sure flash is enabled by doing an explicit check to make sure:
					if ($.fn.flash.hasFlash(7)) {
						baseObject.find("h1.corporate, h2.corporate, h3.corporate, label.corporate").sifr({
							path: baseURL+"/global/flash/",
							font: "sifr",
							textAlign: "left"
						});
					}
				}



				/* 5-star Rating systems
				----------------------------------------------------*/
				if (options.starRaters) {
					baseObject.find("fieldset.rating").starRating();
				}
				
				/* pngFix
				----------------------------------------------------*/
				if (options.pngFix && parseInt($.browser.version) == 6 && $.browser.msie) {
					baseObject.find(".pngFix").ifixpng();
				}
			
			});
		
		}
		
	});


	//custom sprint jQuery widgets
	//add the filter widget to jQuery's functions
	$.fn.filterWidget = function(o) {
		//for each matched element, apply the filterWidget constructor
		return this.each(function() {
			new $.filterWidget(this, o);
		});
	};

	/**
     * The filterWidget object.
     *
     * @constructor
     * @name $.filterWidget
     * @param Object e The element to create the filter for.
     * @param Hash o A set of key/value pairs to set as configuration properties.
     * @cat Plugins/filterWidget
     */
	$.filterWidget = function(e, o){

		this.filterSelector = o.filterSelector;
		this.categorySelector = o.filterCategorySelector;
		this.categoryANDSelector = o.filterCategoryANDSelector;
		this.targetSelector = o.filterTargetSelector;
		this.ANDCategories = o.ANDCategories;
		this.filterCallback = o.filterCallback;
		this.filterPreFunction = o.preFilterFunction;
		this.andFilters = $(o.ANDCategories);
		this.targets = $(o.filterTargetSelector);
		
		var widget = this;
		
		$(this.filterSelector).bind( "click", function(e){
			widget.filter();
		});
		
		this.filter();
		
		return this;
	};


	//the filter functions
    $.filterWidget.fn = $.filterWidget.prototype = {
        filterWidget: '0.1'
    };

    $.filterWidget.fn.extend = $.extend;

    $.filterWidget.fn.extend({

		filter : function(){

			if( this.filterPreFunction ){	this.filterPreFunction(); }
			
			if( this.ANDCategories ){
				this.filterWithAndingCategories();
			} else {
				this.filterWithOringCategories();
			}
			
			if( this.filterCallback ){	this.filterCallback();	}
			
			return this;
		},//end of filter function

		filterWithOringCategories : function(){
			var theFilters = $(this.filterSelector+":checked");
			var andFilterCategories = $(this.categoryANDSelector);
			var filterSelector = this.filterSelector;
			var ANDCategorySelector = this.categoryANDSelector;
			
			this.targets.hide();
			
			//for each target, see if it needs to be filtered (OR FILTERING)
			this.targets.each( function(i){
				var theTarget = $(this);
				
				//OR filters
				theFilters.each( function(i){
					var _this = $(this);
					if( theTarget.hasClass( _this.attr("id") ) && _this.parents(ANDCategorySelector).length==0 ) {
						theTarget.show();
						return true;
					}
				});

				//AND filters
				andFilterCategories.each( function(i){
					$(this).find(filterSelector+":checked").each( function(i){
						if( !theTarget.hasClass( $(this).attr("id") ) && theTarget.css("display") != "none" ){
							theTarget.hide();
							return true;
						}
					});
				});
				
			});
		},//end of filterWithOringCategories function
		
		filterWithAndingCategories : function(){
			//cache vars
			var filterSelector = this.filterSelector;
			var targetSelector = this.targetSelector;
			var categoryANDSelector = this.categoryANDSelector;
			
			//show all targets...we'll hide the ones that don't need to be shown next
			this.targets.show();
			
			//for each category, hide anything that doesn't have at least one of the filters
			$(this.categorySelector).each( function(i){
				var _this = $(this);
				//record if this is an AND category
				var ANDCategory = false;
				if( _this.hasClass(categoryANDSelector) ){	ANDCategory = true; }
				
				//for each checked filter, grab the class we're looking for
				var filterClasses = new Array();
				_this.find(filterSelector+":checked").each( function(i){
					filterClasses.push( $(this).attr("id") );
				});
				
				//if we have no filterClasses, don't bother trying to filter
				if( filterClasses.length < 1 ){
					return true;	//this will go to the next filter category
				}
				
				//for each visible target, see if it has one of the classes
				$(targetSelector+":visible").each( function(j){
					var _this = $(this);
					//if we're ANDing the filters in this category
					if(ANDCategory){

						//if we don't find one of the classes...hide this guy
						for( var x in filterClasses ){
							if( !_this.hasClass(filterClasses[x]) ){
								_this.hide();
								return true; //this will go to the next target
							}
						}
						
					//this bit for if we are oring the fitlers in this category
					} else {
						//if one of the classes is found, we short-circuit this function
						for( var x in filterClasses ){
							if( _this.hasClass(filterClasses[x]) ){
								return true; //this will go to the next target
							}
						}

						//this line will only be run if this target doesn't have one of the classes
						_this.hide();
					}
				});

			});
		}//end of filterWithAndingCategories function

	});// end of $.filterWidget.fn.extend
	
})(jQuery);