//Ensure that jQuery won't interfere with other JavaScript libraries, particularily those being used in 3rd party applications.
jQuery.noConflict();

// define global omniture variable
var _naq=_naq||[];

// Sprint Namespace
var Sprint = {
	baseUrls:{
		'www':'http://www.sprint.com',
		'shop':'http://shop.sprint.com',
		'mysprint':'https://mysprint.sprint.com',
		'lounge':'https://manage.sprintpcs.com',
		'support':'http://support.sprint.com',
		'community':'http://community.sprint.com'
	},
	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: {}
	},
	
	ASmodal: {
		elem: {},
		dimensions: {}
	},
	
	tooltip: {
		elem: {}
	},
	
	shade: {},
	
	ieShims: {
		currentShims: 0
	},
	frmpreferences: {
		browserChkFrmPref: 0,
		bulProcessChanges: {}
	},
	flashComm: {

		trackVideoPlay:function(args) {
			// TECH IMPLEMENTATION, keep this commented out
			if (typeof(Analytics) !== "undefined") {
				var videoTitle = Sprint.fn.getVideoFileDetails(args);
				Analytics.Support.trackVideoPlay(videoTitle);
			}
		},

		trackVideoProgress25:function(args) {
			// TECH IMPLEMENTATION, keep this commented out
			if (typeof(Analytics) !== "undefined") {
				var videoTitle = Sprint.fn.getVideoFileDetails(args);
				Analytics.Support.trackVideoProgress25(videoTitle);
			}
		},

		trackVideoProgressComplete:function(args) {
			// TECH IMPLEMENTATION, keep this commented out
			if (typeof(Analytics) !== "undefined") {
				var videoTitle = Sprint.fn.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 = String(fieldValue.substr(i, 1)).toLowerCase();

					//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;
		
		},
		/*
			Username_11_3
			* Username criteria updated on 7/2011
			- (1) 6-33 characters (alphabets, numbers, special characters)
			- (2) No Whitespace at the end or the beginning of the username						
			- (3) Do Not Allow phone number
			- (4) Do Not Allow eMail address
			- (5) Less than half the string can be repeat characters
			- (6) Allow special characters: periods, hyphens, underscores
			- (7) No Profanity
			- expects the field to be passed in as a jQuery object
		*/
		username_11_3: 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, 4, 6
			var allNumbers = /^[0-9\(\)\.\-_]{6,33}$/; //Covers requirements 3
			var profanity = ["ass-lick","ass_lick","ass.lick","asshat","asshole","assholes","asskisser","asswipe","bastard","beastiality","beastility","beaver","belly-whacker","belly_whacker","belly.whacker","bestial","bestiality","bitcher","bitchers","bitches","bitchin","bitching","blow-job","blow_job","blow.job","blowjob","blowjobs","bonehead","brown-eye","brown_eye","brown.eye","browneye","browntown","bucket-cunt","bucket_cunt","bucket.cunt","bull-shit","bull_shit","bull.shit","bullshit","bullshiit","bung-hole","bung_hole","bung.hole","butt-breath","butt_breath","butt.breath","butt-fucker","butt_fucker","butt.fucker","butt-hair","butt_hair","butt.hair","buttface","buttfuck","buttfucker","butthead","butthole","buttpicker","circle-jerk","circle_jerk","circle.jerk","cocksuck","cocksucked","cocksucker","cocksucking","cocksucks","cooter","cummer","cumming","cumshot","cunilingus","cunillingus","cunnilingus","cuntlick","cuntlicker","cuntlicking","cyberfuc","cyberfuck","cyberfucked","cyberfucker","cyberfuckers","cyberfucking","dildos","dipshit","douche","douche-bag","douche_bag","douche.bag","dumbass","ejaculate","ejaculated","ejaculates","ejaculating","ejaculatings","ejaculation","fagget","fagging","faggit","faggot","fagots","farted","farting","fartings","fatass","felatio","fellatio","fingerfuck","fingerfucked","fingerfucker","fingerfuckers","fingerfucking","fingerfucks","fistfuck","fistfucked","fistfucker","fistfuckers","fistfucking","fistfuckings","fistfucks","fucccked","fucccker","fucccking","fucccks","fuccked","fuccker","fuccking","fucckked","fucckker","fucckking","fucckks","fuccks","fucked","fucker","fuckers","fuckface","fuckin","fucking","fuckings","fuckme","fukced","fuking","fukked","fukker","fukking","fukkked","fukkker","fukkking","fukkks","furburger","gangbang","gangbanged","gangbangs","gaysex","gazongers","godamn","goddamn","gonads","guinne","hardcoresex","horniest","horseshit","hotsex","jack-off","jack_off","jack.off","jackass","jacking-off","jacking_off","jacking.off","jackoff","jerk-off","jerk_off","jerk.off","kickass","kondum","kondums","kummer","kumming","kunilingus","lesbian","love-nest","love_nest","love.nest","lusting","mothafuck","mothafucka","mothafuckas","mothafuckaz","mothafucked","mothafucker","mothafuckers","mothafuckin","mothafucking","mothafuckings","mothafucks","motherfuck","motherfucked","motherfucker","motherfuckers","motherfuckin","motherfucking","motherfuckings","motherfucks","nigger","niggers","pecker","phonesex","phuked","phuking","phukked","phukking","pissed","pisser","pissers","pisses","pissin","pissing","pissoff","pricks","pussies","pussys","retard","schlong","sheister","shiteating","shited","shitface","shitfull","shithead","shiting","shitings","shitted","shitter","shitter","shitters","shittier","shitting","shittings","shitty","sleaze","snatch","wetback"];
			
			//First, test to see if the field value passes the base ruleset and is not all numbers
			if (baseRule.test(fieldValue) && !allNumbers.test(fieldValue)) {
				
				//Field passes one of the tests, check for repeating characters
				var numCharacters = fieldValue.length;
				
				var prevChar = "";
				var sameCharCount = 1;
				
				//Loop through the characters 1 by 1
				for (var i = 0; i < numCharacters; i++) {
					
					var currentChar = String(fieldValue.substr(i, 1)).toLowerCase();

					//Check this character to see if it's the same as the last...
					if (currentChar == prevChar) {
						sameCharCount++;

						//This covers requirement 5
						if (sameCharCount >= Math.ceil((numCharacters)/2)) {
							validUserName = false;
							break;
						}
					}
					else {
						sameCharCount = 1;
					}

					prevChar = currentChar;
				}
				
				//Loop through profanity list and look for matches
				for (var i = 0; i < profanity.length; i++) {
					//This covers requirement 7
					if(fieldValue.toLowerCase() == profanity[i]){
						validUserName = false;
						break;
					}	
				}
			}
			else {
				validUserName = false;
			}			
			
			return validUserName;
		
		},
		/*
			Legacy Username
			- (1) 1-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
		 */
		legacyUsername: function(field) {
			
			var validUserName = true;
			
			var fieldValue = field.val();
			
			var baseRule = /^[a-zA-Z0-9\.\-_]{1,33}$/; //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 = String(fieldValue.substr(i, 1)).toLowerCase();
					
					//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
			- 2-60 of these characters: a-z, A-Z, 0-9, _, -, .: [a-zA-Z0-9_\.\-]{2,60}
				- ensures that display names will be 6-50 chars
			- Or be an email
			- Alphabets, numbers, special characters
		*/
		displayName: /^[a-zA-Z0-9_\.\-]{2,60}$|^[a-zA-Z0-9_\.\-]+\@([a-zA-Z0-9\-]+\.)+[A-Za-z]{2,6}$/,
		
		/*
			- Account Number
			- Only AlphaNumerics
		*/
		accountNumber: /^[A-Za-z0-9]{2,20}$/,
		
		/*
			Nickname
			- 1-59 of these characters: a-z, A-Z, 0-9, _, -, ., \s(spaces): [a-zA-Z0-9_\.\-]{1,59}
				- ensures that display names will be 2-60 chars
			- Or be an email
		*/
		//nickName: /^[a-zA-Z0-9_\.\-]{1}[a-zA-Z0-9_\.\-\s]{1,59}$|^[a-zA-Z0-9_\.\-]+\@([a-zA-Z0-9\-]+\.)+[A-Za-z]{2,6}$/,
		nickName: /^.{1}.{1,59}$|^[a-zA-Z0-9_\.\-]+\@([a-zA-Z0-9\-]+\.)+[A-Za-z]{2,6}$/,
		/*
			Friendlyname
			- 1-99 of these characters: a-z, A-Z, 0-9, _, -, ., \s(spaces): [a-zA-Z0-9_\.\-]{1,59}
				- ensures that display names will be 2-100 chars
			- Or be an email
		*/
		friendlyName: /^[a-zA-Z0-9_\.\-]{1}[a-zA-Z0-9_\.\-\s]{1,99}$|^[a-zA-Z0-9_\.\-]+\@([a-zA-Z0-9\-]+\.)+[A-Za-z]{2,6}$/,
		
		/*
			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;

		},
		
		/*
			Password_11_3
			* Username criteria updated on 7/2011
			- (1) 6-33 characters (alphabets, numbers, special characters)
			- (2) Must contain at least one capital letter and one number
			- (3) Can not repeat a character for half the password or more (e.g. aaaaB1#2)
			- (4) Can not use sequential characters for half the password or more (e.g., abcd1Co8, Coff7890)
			- (5) No spaces
			- (6) Allow special characters: periods, hyphens and underscore

			- expects the field to be passed in as a jQuery object
		*/
		password_11_3: function(field) {

			var validPassword = true;
			var fieldValue = field.val();
			//var baseRule = /^(?=.*\d)(?=.*[A-Z])[a-zA-Z0-9\.\-_]{6,33}$/; //Covers requirements 1, 2, 5, and 6
			var baseRule = /^[a-zA-Z0-9\.\-_]{6,33}$/; //Covers requirements 1, 2, 5, and 6
			var baseRule_capital = /[A-Z]+/;
			var baseRule_digits = /\d+/;
			var numSequence = "012345678901234567890123456789012";
			var alphaSequence = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnop";

			//First, test to see if the field value passes the base ruleset
			if (baseRule.test(fieldValue) && baseRule_capital.test(fieldValue) && baseRule_digits.test(fieldValue)) {
				
				
				//Field passes one of the tests, check for repeating characters
				var numCharacters = fieldValue.length;
				
				var prevChar = "";
				var sameCharCount = 1;
				var halfOrMore = Math.ceil((numCharacters)/2);
				
				//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 3
						if (sameCharCount >= halfOrMore) {
							validPassword = false;
							break;
						}
					}
					else {
						sameCharCount = 1;
					}

					prevChar = currentChar;
					
					//match number and alpha sequences
					if(i <= halfOrMore){
						var testStr = new RegExp(fieldValue.substr(i,halfOrMore).toLowerCase());
						var numMatch = numSequence.match(testStr);
						var alphaMatch = alphaSequence.match(testStr);
						
						//This covers requirement 4
						if(numSequence.match(testStr) || alphaSequence.match(testStr)){
							validPassword = false;
							break;
						}
					}
				}
			}
			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}$/,
		
		/*
			Enterprise Email Address: Same rules as email address above plus additional rules
			- Starts with 1 or more of these charcters a-z, A-Z, 0-9, _, -,: [a-zA-Z0-9_\-]+
			- Optionally has a period followed by another character or characters (?:\.[a-zA-Z0-9_\-]+)*
			- Followed by an @ sign: \@
			- Followed by 1 or more groups of the following characters ending in a period but not starting with 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}
		*/
		enterpriseEmailAddress: /^[a-zA-Z0-9_\-]+(?:\.[a-zA-Z0-9_\-]+)*\@([a-zA-Z0-9\-]+\.)+[A-Za-z]{2,6}$/,
	
		/*
			Domain Name
			- 1 or more groups of the following characters a-z, A-Z, 0-9, -, ending in a period, 3-63 characters long: ([a-zA-Z0-9\-]{3,63}\.)+
			- an optional 2 character country code
			- Followed by 2-6 of the following characters a-z, A-Z: [A-Za-z]{2,6}
		*/
		
		domainName: /^([a-zA-Z0-9\-]{3,63}\.)+(?:[coCO]{2}\.)?[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;
		},
		
		/*
			TextArea
			- returns true if there is more than one character entered
		*/
		textArea: function(field) {
			if (field.val().length > 1) {
				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: \-
			- 1 - 10 digits: \d{1,10}
		*/		
		DAC: /^\d{9}\-\d{1,10}$/,	
		/*
			PIN (personal identification number)
			- 6-10 digits: \d{6,10}
		*/
		PIN: /^\d{6,10}$/,
		/*
			PIN_min (personal identification number) - minimal length
			- 1 digit required, any number of digits to follow: \d{1,}
			- Maxlength is set by the input element attribute
		 */
		PIN_min: /^\d{1,}$/,
		
		/*
			SIM
			- 15 digits: \d{15}
		*/
		SIM: /^\d{15}$/,
		
		/*
			MAC
			- 12 alphanumeric: \d{12}
		*/
		MAC: /^[a-zA-Z0-9_\.\-]{12}$/,
		
		/*
			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: /^[A-Za-z0-9\s]{2,30}$/,
		/*
			Security Hint Answer for Checkout Flow, no special characters
			- Any 2-20 characters, A-Z, a-z, 0-9: .{2,20}
			- Dont allow any special characters
		*/
		securityHintAnswer2: /^[A-Za-z0-9\s]{2,20}$/,
		
		/*
			Security Answer
			- Alphanumeric 2-30 characters: \w{2,30}
		*/
		securityAnswer: /^[\w\s]{2,30}/,
		
		/*
			First Name
			- In 10.4 its 0-30 characters
			- Up to 50 characters in length, minimum of 2
			- Must start with 1 of these characters: a-z, A-Z, 0-9, _, -, .: [a-zA-Z0-9_\.\-]{1}
			- 0-48 of these characters: a-z, A-Z, 0-9, _, -, ., spaces: [a-zA-Z0-9_\.\-\s]{0,48}
			- Must end with 1 of these characters: a-z, A-Z, 0-9, _, -, .: [a-zA-Z0-9_\.\-]{1}
		*/
		firstName: /^[a-zA-Z0-9_\.\-]{1}[a-zA-Z0-9_\.\-\s]{0,48}[a-zA-Z0-9_\.\-]{1}$/,
		/*
			Subscriber Name
			- Up to 50 characters in length, minimum of 2
			- Must start with 1 of these characters: a-z, A-Z, 0-9, _, -, .: [a-zA-Z0-9_\.\-]{1}
			- 0-48 of these characters: a-z, A-Z, 0-9, _, -, ., spaces: [a-zA-Z0-9_\.\-\s]{0,48}
			- Must end with 1 of these characters: a-z, A-Z, 0-9, _, -, .: [a-zA-Z0-9_\.\-]{1}
		*/
		subscriberName: /^[a-zA-Z0-9_\.\-]{1}[a-zA-Z0-9_\.\-\'\\s]{0,48}[a-zA-Z0-9_\.\-]{1}$/,
		/*
			Initial
			- 1-32 of these characters: a-z, A-Z: [a-zA-Z]{1,32}
		*/
		initial: /^[a-zA-Z0-9_\.\-]{1,32}$/,
		
		/*
			Last Name
			- Up to 60 characters in length, minimum of 2
			- Must start with 1 of these characters: a-z, A-Z, 0-9, _, -, .: [a-zA-Z0-9_\.\-]{1}
			- 0-58 of these characters: a-z, A-Z, 0-9, _, -, ., spaces: [a-zA-Z0-9_\.\-\s]{0,58}
			- Must end with 1 of these characters: a-z, A-Z, 0-9, _, -, .: [a-zA-Z0-9_\.\-]{1}
		*/
		lastName: /^[a-zA-Z0-9_\.\-]{1}[a-zA-Z0-9_\.\-\s]{0,58}[a-zA-Z0-9_\.\-]{1}$/,

		/*
			Company Name
			- 2-35 of these characters: a-z, A-Z, 0-9, -, .: [a-zA-Z0-9\.\-]{2,30}
			- also supports .,&,@,- for company name
		*/
		companyName: /^[A-Za-z0-9\.\&\@\-\s]{2,35}$/,
		
		
		/*
			Ecenter Business user company name
			Which should support ., & for the company name
			Eg: Wine & Flowers Monsters Inc.
		*/
		ecenterCompanyName: /^[A-Za-z0-9\.\&\@\-\s]{2,30}$/,
		
		/*
			Phone Number Section Requiring 3 Digits
			- 3 digits: \d{3}
		*/
		phoneNumber3Digits: /^\d{3}$/,
		
		/*
			Phone Number Section Requiring 4 Digits
			- 4 digits: \d{4}
		*/
		phoneNumber4Digits: /^\d{4}$/,
		
		/*
			Phone Extension Number
			- 1-4 digits: \d{1,4}
		*/
		phoneExtension: /^\d{1,4}$/,
		
		/*
			Phone Short Code Number
			- 4-8 digits: \d{1,4}
		*/
		phoneShortCode: /^\d{4,8}$/,
		
		/*
			Phone Digits Duplicate and Junk Number Check
			 - Pass entire 10 digit number
			 - Checks for "1111111111" and "1234567890" as invalid numbers
		   - Allows ranges through ^[2-9] at the beginning
		   
		*/
		phoneNumberDuplicates: /^[2-9]\d{2}\d{3}\d{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}$/,
		
		/*
			Apartment Number
			- Any 1-10 characters: .{1,10}
		*/
		apartment: /^.{1,10}$/,
		
		/*
			City
			- Any 2-26 characters: .{2,26}
		*/
		city: /^.{2,26}$/,

		/*
			City50
			- Any 2-50 characters: .{2,50}
			- This set of validation will be used for checkout flow.
		*/
		city50: /^.{2,50}$/,
		
		/*
			ZIP Code
			- 5 digits: \d{5}
		*/
		zipCode: /^\d{5}$/,
		/*
		 As part of PJ005466 we have free zipcode 
		  Free ZIP Code
			- sould accept digits and characters
			- Should not be special characters
		*/
		freeZipCode: /^[a-zA-Z0-9_\.\-]{1}[a-zA-Z0-9_\.\-\'\\s]{0,48}[a-zA-Z0-9_\.\-]{1}$/,
		/*
			Tax ID Number (TBD)
			- 2 digits: \d{2}
			- Optional Space, Period or Dash [\s|\-|\.]?
			- 4 digits: \d{4}
		*/
		taxId: /^\d{2}[\s|\-|\.]?\d{4}$/,
		
		/*
			9-Digit Tax ID Number (TBD)
			- 2 digits: \d{2}
			- Optional Space, Period or Dash [\s|\-|\.]?
			- 7 digits: \d{7}
		*/
		taxId9: /^\d{2}[\s|\-|\.]?\d{7}$/,
		
		/*
			Voicemail Passcode
			- 4-10 digits
		*/
		voicemailPasscode: /^\d{4,10}$/,
		
		/*
				SSN
			-  looks for 3-2-4 format
			-  no repetitive numbers allowed
			- no consecutive numbers allowed
			- invalid for Greater than or equal to 773 on the first 3 digits.
			
		*/
		ssn: /^((?!000)(?!666)([0-6]\d{2}|7[0-6]\d|77[0-2]))((?!00)\d{2})((?!0000)\d{4})$/,
		
		
		/*
		  Credit Card Number
		  - 16 Digits
		  - optional Separator "-": xxxx-xxxxxx-xxxxx allowed
		*/
		
		cardnumber: /^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$/,
		
		/*
			Security Code
			- CVV 3 or 4 digits
			- Numbers only.
		 
		*/
		cvv: /(?!000)\d{3,4}$/,
		
		/*
			Price
			- One or more digits
			- Optional cents shown as exactly two digits
		*/
		price: /^[0-9]+(\.[0-9]{2})?$/
		
	},
	
	//Helper functions
	fn: {
		/*
		* getContentString
		*
		* function to get a string from the Sprint.content object
		*
		* @param key: String: the child object to look for in the Sprint.content object (ie: "modal.closeButtonAltText");
		*
		* @returns String - either the value we're looking for, or empty quotes "" if the value can't be found
		*/
		getContentString: function(key) {
		
			var returnString;
			
			try {
				returnString = eval("Sprint.content."+key+"['"+Sprint.currentLanguage+"']");
			}
			catch(err) {
				returnString = "";
			}
			
			return returnString;
		
		},

		/**
		 * Formats price amount with commas.
		 * @param price Price value.
		 * @return Returns formatted amount with 2 cent decimals.
		 */
		formatPrice: function(price) {
			var bits = price.toString().split(".");
			var dollars = bits[0];
			var cents = bits[1];
			var dollarsBits = [];
			var index = dollars.length;

			// get blocks of 3
			while (index >= 3) {
				dollarsBits.unshift(dollars.substring(index - 3, index));
				index -= 3;
			}

			// add what's left
			if (index > 0)
				dollarsBits.unshift(dollars.substring(0, index));

			// fix the cents amount, when there are no cents at all,
			if (cents == null || cents.length == 0)
				cents = "00";
			// ...when it's just one decimal
			else if (cents.length < 2)
				cents += "0";
			// ...when it's more than 2 decimals
			else if (cents.length > 2)
				cents = cents.substring(0, 2);

			// join with commas
			dollars = dollarsBits.join(",");

			return dollars + "." + cents;
		},
		
		/*
		* 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) {
			if (event !== undefined && event.hasOwnProperty('status') && event.status !== 0 && event.readyState !== 4) {
				if (event.status === 400) window.location.href = "/mysprint/pages/sl/common/400.jsp";
				else if (event.status === 403) window.location.href = "/mysprint/pages/sl/common/403.jsp";
				else if (event.status === 404) window.location.href = "/mysprint/pages/sl/common/404.jsp";
				else if (event.status === 500) window.location.href = "/mysprint/pages/sl/common/500.jsp";
				else if (event.readyState === 0) window.location.href = "/mysprint/pages/sl/common/err.jsp";
				else window.location.href = "/mysprint/pages/sl/common/404.jsp";
			}
		},
		
		
		/*
		* validateSession
		* 
		* function that intercepts ajax calls - checks whether current session is valid 
		*
		* @param sessionObj: object containing variables required to determine session validity.
		* 
		* @redirects: false if sessionObj is null, redirects user if session is not valid.
		*
		*/
		validateSession: function(sessionObj){
			if (sessionObj !== undefined && sessionObj !== null 
				&& typeof(sessionObj) === 'object' && sessionObj.hasOwnProperty('sessionValidated') 
					&& !sessionObj.sessionValidated && sessionObj.hasOwnProperty('sessionRedirectUrl')) {
						if (Sprint.modal.elem && Sprint.modal.elem.length > 0) Sprint.modal.elem.closeModal();
						window.location.href = sessionObj.sessionRedirectUrl;
			}
		},
		
		/*
		* 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 = {				
			};
			
			//var teaLeafArray = new Array();
			
			function addErrorMessage(fieldName, errorMessage, fieldLabel, additionalFields, errors) {
				formErrors[fieldName] = {
					name: fieldName,
					errorMessage: errorMessage,
					fieldLabel: fieldLabel,
					additionalFields: additionalFields,
					multipleErrors: errors
				};
				
				// TEALEAF SESSION TRACKING
				if ($("#"+fieldName).length >0 && typeof(TeaLeaf) != "undefined" &&  typeof(TeaLeaf.Client) != "undefined") {
					var validationEvent = new TeaLeaf.Event(TeaLeaf.$C("UIEventAppInfo"), "errorValidation");
					var teaLeafArray = new Array();
					var value = $.trim($("#"+fieldName).attr("value"));
					if (value == ""){
						value = "emptyStr";
					}
					teaLeafArray = ["fieldName",TeaLeaf.Event.tlFormatXML(fieldName),
									"errorMessage", TeaLeaf.Event.tlFormatXML(errorMessage),
									"value",TeaLeaf.Event.tlFormatXML(value)];
					validationEvent.tlAddData(teaLeafArray);
					validationEvent.tlSend();
				}
			}
			
			//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 and it isn't disabled before trying to validate it (this supports forms that have different fields in different scenarios.
				if (currentFieldObj.length > 0 && !currentFieldObj.is(':disabled')) {
					
					//var currentFieldValue = $.trim(currentFieldObj.val());
					if(typeof currentFieldObj.val() == "object"){
						//add empty string to convert object to string - for IE browsers
						var currentFieldValue = $.trim(currentFieldObj.val() + "");
					} else {
						var currentFieldValue = $.trim(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, currentField.fieldLabel, currentField.additionalFields);
					}
					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, currentField.fieldLabel, currentField.additionalFields, currentField.multipleErrors);
							}
							else if (currentField.customValidationRule.test && !currentField.customValidationRule.test(currentFieldValue)) {
								formValid = false;
								
								//Add the error to the formErrors object
								addErrorMessage(currentField.name, currentField.invalidErrorMessage, currentField.fieldLabel, currentField.additionalFields);
							}
						}
						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, currentField.fieldLabel, currentField.additionalFields);
								}
							}
							else if (currentField.type != "match" && currentField.type != "caseInsensitiveMatch") {
								//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, currentField.fieldLabel, currentField.additionalFields);
								}
								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, currentField.fieldLabel, currentField.additionalFields);
									}
									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, currentField.fieldLabel, currentField.additionalFields);
									}
	
								}
							}
							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, currentField.fieldLabel, currentField.additionalFields);
							}
							else if (currentField.type == "caseInsensitiveMatch" && currentFieldValue.toLowerCase() != form.find("[name='"+currentField.mustMatch+"']").val().toLowerCase()) {
								//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, currentField.fieldLabel, currentField.additionalFields);
							}
						}
					}
				}
			}
			
			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;
		},
		
		/*
		* getVideoFileDetails
		*
		* Function to get details about the video that is being tracked by analytics.
		*
		* @params args: the arguments for the video file
		*
		* @returns the title of the video file
		*/
		getVideoFileDetails: function(args) {
			var title = "";
			if (typeof(Analytics) !== "undefined") {
			
				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;
		},
		
		/*
		* The following four methods create, read, appendTo and delete cookies.
		* It does not use any jQuery, strict JavaScript.
		*/
		createCookie: function(name, value, days, path, domain) {
			if (days) {
				var date = new Date();
				date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
				var expires = "; expires=" + date.toGMTString();
			}
			else {
				var expires = "";
			}
			
			document.cookie = name + "=" + value + expires + "; path=" + path +"; domain=" + domain;
		},

		readCookie: function(name) {
			var cName   = name + "=";
			var cArray  = document.cookie.split(';');
			var cACount = cArray.length;
			
			while (cACount) {
				cACount--;
				var c = cArray[cACount];
				while (c.charAt(0) == ' ') c = c.substring(1, c.length);
				if (c.indexOf(cName) == 0) return c.substring(cName.length, c.length);
			}
			
			return null;
		},
		
		appendToCookie: function(name, value, days, path, domain) {
			Sprint.fn.createCookie(name, Sprint.fn.readCookie(name) + value, days, path, domain);
		},
		
		removeValueFromCookie: function(name, value, days, path, domain) {
			var oldCookieValue = Sprint.fn.readCookie(name);
			var newCookieValue = oldCookieValue.replace(value, "");
			Sprint.fn.createCookie(name, newCookieValue, days, path, domain);
		},

		deleteCookie: function(name,domain) {
			Sprint.fn.createCookie(name, "", -1, "/", domain);
		},
		
		/*
		* validateDate
		*
		* Function to tell whether a user-entered date is actually valid (ie Feb 31, 2010 is NOT valid).
		*
		* @param day: the day the user entered (int)
		* @param month: the month the user entered (int)
		* @param year: the year the user entered (int)
		*
		* @returns boolean: true if date is valid, false if not.
		*/
		validateDate: function(day, month, year) {

			//javascript months start at 0 (0-11 instead of 1-12), so we'll subtract 1 right away
			month--;

			//set up a Date object based on the day, month and year arguments
			var enteredDate = new Date(year, month, day);

			/*
			Javascript Dates are a little too forgiving and will change the date to a reasonable guess if it's invalid. We'll use this to our advantage by creating the date object and then comparing it to the details we put it. If the Date object is different, then it must have been an invalid date to start with...
			*/
			return ((day == enteredDate.getDate()) && (month == enteredDate.getMonth()) && (year == enteredDate.getFullYear()));
		}
	}
	
};

//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($) {
	
	$.browser.getIEversion = function (){
		if (document.body.style.scrollbar3dLightColor!=undefined) {
			if (document.body.style.opacity!=undefined) {return 9;}
			else if (document.body.style.msBlockProgression!=undefined) {return 8;}
			else if (document.body.style.msInterpolationMode!=undefined) {return 7;}
			else if (document.body.style.textOverflow!=undefined) {return 6;}
			else {return 5.5;}
		} else {
			return null;
		}
	};
	
	//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
				reposition:			true	 // boolean: set this value to false to supress the repositioning of the modal on resize - useful with disclosures
			};

			var options = $.extend(defaults, scriptOptions);

			// Store the modal options
			Sprint.modal.options = options;

			// 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);

			Sprint.modal.getDimensions = function() {
				var docWidth 	= $(document).width();
				var docHeight 	= $(document).height();
				var winWidth 	= $(window).width();
				var winHeight 	= $(window).height();
				var bodyWidth 	= $("body").width();
				var bodyHeight 	= $("body").height();
				var overlayWidth = function(){
						var returnWidth = ($.browser.msie && $.browser.getIEversion() < 7)? bodyWidth : docWidth;
						if ($.browser.msie && $.browser.getIEversion() == 8){
							if((winWidth + 4) == docWidth){
								returnWidth = winWidth;
							}
						}
						return returnWidth;
					}();
				var overlayHeight = function(){
						var returnHeight = (docHeight < winHeight) ? winHeight : docHeight;
						if ($.browser.msie && $.browser.getIEversion() == 8) {
							if((winHeight + 4) == docHeight){
								returnHeight = winHeight;
							}
						}
						return returnHeight;
					}();
					
				var dimensions = {
					docWidth:	docWidth,
					docHeight:	docHeight,
					winWidth:	winWidth,
					winHeight:	winHeight,
					bodyWidth:	bodyWidth,
					bodyHeight:	bodyHeight,
					overlayWidth: overlayWidth,
					overlayHeight:overlayHeight
				};
				
				return dimensions;
			};
			
			var dimensions = Sprint.modal.getDimensions();

			// 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.fn.getContentString("modal.closeButtonAltText")+"\"></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 current 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() {
					
					try{
						//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'], textarea, button",
										containFocus: true
									});
								}
	
							//}, 1); Set a very quick timeout so that this definitely fires AFTER the modal opens.
							// Commented out as it was causing unnessessary delay in IE6
						}
					}catch(e){
						// clear out the modal and post an error message, chat link or contact us form
						$("#modalHolder .modalContent").html('<h4>'+Sprint.fn.getContentString("modal.loadError")+'</h4><h5>'+e.message+'</h5>');
						
						// try to bind the close event to any "closer" elements.
						$(this).find(".closeModal").unbind("click").bind("click", function(event) {
							//Close the modal
							$("#modalOverlay,#modalHolder").remove();
							return false;
						});
						
						// alert with error message
						alert(Sprint.fn.getContentString("modal.loadError")+e.message);
					}
					
					// 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;
						});
					}
				};

				
				// Don't animate in IE
				if ($.support.opacity) {
					Sprint.modal.elem.fadeIn("fast", afterOpen);
				}
				else {
					Sprint.modal.elem.show();
					afterOpen();
				}
				
				// try to bind the close event to any "closer" elements.
				Sprint.modal.elem.find(".closeModal").bind("click", function(event) {
					// TEALEAF SESSION TRACKING
					if (typeof(TeaLeaf) != "undefined" &&  typeof(TeaLeaf.Client) != "undefined") {
						TeaLeaf.Client.tlAddEvent(event);
					}
					//Close the modal
					Sprint.modal.elem.closeModal();

					//Fire off the closeCallback function if one has been defined
					if (options.closeCallback) {
						options.closeCallback();
					}
					
					return false;
				});

				// If window was resized, calculate & reset the overlay dimensions
				$(window).bind("resize", function() {
					//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() {
				// Remove the modal
				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");
				
				// TEALEAF SESSION TRACKING
				if (typeof(TeaLeaf) != "undefined" &&  typeof(TeaLeaf.Client) != "undefined") {
					TeaLeaf.Client.tlProcessNode(document);
				}
			};

			//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) {
			
			//clear previously initialized timers
			if (Sprint.modal.doResize) {
				clearTimeout(Sprint.modal.doResize);
			}
			
			//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
			} else {
				var options = $.extend(Sprint.modal.options, options);
			}

			// determine the width of the modal based on the modal content
			var modalWidth = $(Sprint.modal.content).width();
			//Make sure the modal is never wider than the default width

			if (modalWidth > options.width) {
				modalWidth = options.width;
			} else {
				//PJ005466: This condition is only for forgot password modal issues 
				if(options.forgot){
					modalWidth = options.width;
				}else {
					modalWidth = modalWidth;
				}
			}
			
			$(".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();
			var scrollTop = $(document).scrollTop();

			// 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 > $(window).height()) {
				var modalMarginTop = 0;
				var modalPositionTop = scrollTop + "px";
			}
			// vertically center the modal in the viewport
			else {
				var modalMarginTop	= ((Math.round(modalChromeHeight / 2) * -1) + 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 > $(window).width()) {
				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%";
			}
			
			if(options.reposition){
				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)
			Sprint.modal.doResize = setTimeout(function() {
				var modalOverlay = $("#modalOverlay");
				modalOverlay.css({width: 0, height: 0}); //Start by resetting overlay width/height to 0
				
				var dimensions = Sprint.modal.getDimensions();
	
				modalOverlay.css({
					width:	 dimensions.overlayWidth,
					height:  dimensions.overlayHeight
				}).removeShim().addShim(749);
			}, 100);

			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.fn.getContentString("modal.loadingText")+"</div>";
			
			//
			// Spinner TIMEOUT was disabled to show the loading graphics immediately
			//
			
			//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",
				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
				beforeShow: null,		// function: callback to run any scripts before tooltip is revealed
				width: null				// integer: a width that can be specified to override css settings
			};

			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);
				var exitTooltipCreation = false;
				
				//prevent multiple tooltip bindings by looking for the namespaced event on this object
				var events = anchor.data("events");
				if(events){
					if(events.click){
						$.each(events.click,function(i){
							if(this.type == "tooltip"){
								exitTooltipCreation = true;
								return;
							}
						});
					}
				}
				if(exitTooltipCreation){
					return;
				}

				if(options.disableClick){
					
					// prevent the link from going anywhere
					anchor.bind("click.tooltip", 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.trigger("click");
							}
							else {
								field.focus();
							}
						}
						
						return false;
					});
				}
				
				var tooltipText = "";
				var tooltipContent = "";
				var anchorTarget = null;
				
				//
				// To support applying tooltip to a non A tag we need to pass tooltip body
				// container ID through the class of the element. We then parse it out, ie:
				//
				// <div class="tooltipBox tooltip_myElementId">...</div>
				// <div id="myElementId">...</div>
				//
				if (anchor.hasClass("tooltipBox")) {
					var matches = /\btooltip_(\w+)\b/.exec(anchor.attr("class"));
					
					if (matches != null) {
						anchorTarget = "#" + matches[1];
					}
				}
				else {
					var relAttrib = anchor.attr("rel");
					var hrefAttrib = $.trim(anchor.attr("href"));
					var customContentAttrib = anchor.attr("tooltipCustom");
				
					if (relAttrib && relAttrib.indexOf("#") == 0) {
						anchorTarget = relAttrib;
					}
					else if (customContentAttrib) {
					// The custom tool tip content is used to allow the content team to 
					// create a tool tip with the content in a custom data attribute.
					//
					// Example: <a class="tooltipAnchor" tooltipCustom="TOOLTIP CONTENT">..</a>
						tooltipContent = customContentAttrib;
						tooltipText = customContentAttrib;
					}
					else {
						//Find the anchor target that this tooltip links to
						anchorTarget = hrefAttrib;

						//stip off everything before the "#"
						anchorTarget = anchorTarget.substr(anchorTarget.indexOf("#"));
					}
				}
				
				//Make sure the anchorTarget isn't just "#"	
				
				if (anchorTarget && String(anchorTarget).length > 1) {
					tooltipText = $(anchorTarget).text();
					tooltipContent = $(anchorTarget).html();
				}
				
				if ($.trim(tooltipText) == "") {
					if (hrefAttrib.indexOf(window.location.pathname) > -1 || hrefAttrib == "" || hrefAttrib.indexOf("#") == 0) {
						var anchorContent = anchor.html();
						var newAchorContent = "<span class='replacedTooltip'>"+ anchorContent +"</span>";
						anchor.after(newAchorContent);
						anchor.remove();
					} 
				} 
				else {
					
					// Show the tooltip on mouseover
					anchor.bind("mouseenter.tooltip", 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");
								if(options.width){
									tooltip.width(options.width);
								}
							}

							//Show the tooltip after a 1/4 second delay
							Sprint.tooltip.showTimer = setTimeout(function() {

								//set the tooltip's content
								tooltip.find(".tooltipContentArea").html(tooltipContent);
								if(typeof(options.beforeShow) == "function"){
									options.beforeShow(e);
								}

								//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) {
												// if tooltip is below the anchor && the tooptip height is greater than the screen height
												if (options.belowAnchor && (tooltipHeight > windowHeight)){
													//only scroll to the top of the tooltip
													tooltip.scrollTo({speed: "slow"});
												} else {
													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("mouseleave.tooltip", 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("mouseenter.tooltip", function() {
								clearTimeout(Sprint.tooltip.timer);
							});

							//rolling out of the tooltip will start the delayed fadeout timer again.
							tooltip.bind("mouseleave.tooltip", 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;
					$(".currentTooltip").removeClass("currentTooltip").unbind("mouseleave.tooltip");
					tooltip.unbind("mouseleave.tooltip");
					tooltip.unbind("mouseleave.tooltip");
					
					clearTimeout(Sprint.tooltip.tooltipScroll); //Prevent auto-scroll if the user rolls off the tooltip.
					
				}

			});

		},
		/*
		* destroy tooltip
		*
		* function to remove namescaped tooltip events from anchor
		*
		*/
		destroyTooltip: function(scriptOptions) {
			return this.each(function() {
				$(this).unbind(".tooltip").removeAttr("tooltipcustom");
			});
		},

		/*
		* 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.fn.getContentString("shade.closedText"),   // string: The text of the shade tab when closed
				openedText:   Sprint.fn.getContentString("shade.openedText"),  // 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
				var content = $(this).html();
				$(this).html("<div class=\"shadeContainer\"><div class=\"headingTabLink\"><a href=\"#\">" + options.closedText + "</a></div></div>");
				$(this).css("display","block");
				
				//Set the width
				$(this).find(".shadeContainer").width(options.width);
				$(this).find(".shadeContainer").html("<div class=\"shadeContent\" style=\"display: none\" >" + content + "</div><div class=\"headingTabLink\"><a href=\"#\">" + options.closedText + "</a></div>");
				
				//Prevent clicks on the shade itself (unless it's the tab) from bubbling up (which would cause the shade to close)
				$(this).find("div:not(.headingTabLink)").bind("click", function(event) {
					event.stopPropagation();
				});
				
				//Event handler for the shade open/close tab
				$(this).find(".headingTabLink").bind("click", function(event) {
					event.stopPropagation(); //prevent bubbling
					
					var shade = $(this).parents(".shade");
					
					//If user is clicking on the shadeTab for the shade that's currently open... close it
					if (shade.find(".shadeContent").css("display") == "block") {
						close(shade);
						unbindEvents();
					}
					//If not, open the requested shade (& close any that may already be open)
					else {
						Sprint.shade.close();
						open(shade);
						bindEvents();
					}
					event.preventDefault(); // don't follow the link
				});
			});

			// event handler that will be set to close the current shade when a user clicks anywhere outside it (unless it's on another shade).
			function handleClick(event) {
				if (!Sprint.shade.isAnimating) {
					event.stopPropagation();
					Sprint.shade.close();
					unbindEvents();
				}
			};
			
			function bindEvents() {
				$("html, body, div").not($(".shade, .shade div")).bind("click", handleClick);
			};
			
			function unbindEvents() {
				$("html, body, div").not($(".shade, .shade div")).unbind("click", handleClick);
			};
			
			function close(shade) {
				if (!Sprint.shade.isAnimating) {
					Sprint.shade.isAnimating = true;
					shade.find(".shadeContainer").removeShim();
					shade.find(".shadeContent").stop().slideUp(options.speed, function() {
						Sprint.shade.isAnimating = false;
						
						shade.find(".headingTabLink")
							.removeShim()
							.find("a")
								.html(options.closedText)
						;
						$(this).removeShim();
					});
				}
			};
			
			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")
							.addShim(1, true)
							.find("a")
								.html(options.openedText)
						;
						$(this).addShim(1, true);
					});
				}
			};
		},

		/*
		* 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.fn.getContentString("accordion.toggleButtonClosedText"),  // string: Toggle button text for when an accordion item is closed.
				toggleButtonOpenedText:  Sprint.fn.getContentString("accordion.toggleButtonOpenedText")  // 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) {	
				// TEALEAF SESSION TRACKING
				if (typeof(TeaLeaf) != "undefined" &&  typeof(TeaLeaf.Client) != "undefined") {
					TeaLeaf.Client.tlAddEvent(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.
				beforeSwitch:          null,           // function: this function will be run before each tab switch
				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.
				allowAnchorClicks:     false           // boolean: set this value to true to allow <a/> clicks from the tab content
			};

			var options = $.extend(defaults, scriptOptions);
			var urlHash = getURLHash();
			var targetTabID = "";
			var flashStore = [];

			return this.each(function() {
				try {
					
				function selectTab(selectedIndex) {

					var selectedTab = $(tabIndex.children("li")[selectedIndex]);
					var lastSelectedTab = $(tabIndex.children("li")[lastSelected]);
					var lastSelectedContent = $(tabbedSection.children(".tabContent")[lastSelected]);
					
					//store tabbed content containing flash video
					if(lastSelectedContent.children("object").size() > 0) {
						flashStore[lastSelected] = lastSelectedContent.html();
						lastSelectedContent.html(flashStore[lastSelected]);
					}
					
					//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();
					}
					
					lastSelected = selectedIndex;
				}
					
				var tabbedSection = $(this);
				
				//Make sure this layer has tab content, if not, ignore this one
				if (tabbedSection.children(".tabContent").length < 1) {
					return;
				}
				
				var defaultTab = 0; //Index of the tab that starts off selected
				var lastSelected = defaultTab;
		
				// Hide Tab Content layers
				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;
					
					//detect URL tab hash
					if(urlHash == tabID && urlHash != ""){
						targetTabID = i;
					}
					
					if (options.useFullTitleContents) {
						tabTitle = $(this).children(".sectionTitle").html();
						newTab = $("<li>"+tabTitle+"</li>");
					}
					else {
						tabTitle = $(this).children(".sectionTitle").text();
						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;
						lastSelected = defaultTab;
					}
					
				});
				
				//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(event) {
					// TEALEAF SESSION TRACKING
					if (typeof(TeaLeaf) != "undefined" &&  typeof(TeaLeaf.Client) != "undefined") {
						TeaLeaf.Client.tlAddEvent(event);
					}
					// allow clicks on the anchor tags
					var originalTarget = event.originalEvent;
					if (originalTarget) {
						originalTarget = $.browser.msie ? originalTarget.srcElement : originalTarget.target;
						if (options.allowAnchorClicks == true && originalTarget && originalTarget.nodeName == 'A')
							return true;
					}

					if (options.beforeSwitch) {
						if (options.beforeSwitch(event) == false){
							return false;
						}
					}
					
					//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) OR the targeted tab
				if (targetTabID == "") {
					selectTab(defaultTab);
				}
				else {
					selectTab(targetTabID);
					$(".sprint:first").scrollTo();
				}
				
				} catch(e){
					alert(e.message);
				}
				
			});
		
			function getURLHash() {
				var urlHashDirty = window.location.hash.slice(1);				
				var urlHash = urlHashDirty.indexOf('?') > -1 ? urlHashDirty.slice(0,urlHashDirty.indexOf('?')) : urlHashDirty;

				return urlHash;
			}
			
		},

		/*
		* scrollTo
		*
		* function that automatically scrolls to a certain element
		*
		* @param scriptOptions: Object, defines options that will override the defaults
		*
		*/
		scrollTo: function(scriptOptions) {
		
			var defaults = {
				target:		   "html, body",	// string: selctor for the DOM element to be scrolled
				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;
			}
			
			$(options.target).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.fn.getContentString("disclosure.openedText"),   // string: The text of the disclosure toggle when the disclosure is open
				closedText:      Sprint.fn.getContentString("disclosure.closedText"),    // 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(i) {
				
				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 disclosure a toggle button
				var toggleButton = $("<a href=\"#\" class=\"disclosureToggle\" id=\"disclosureToggleButton_"+i+"\">"+toggleButtonText+"</a>");
				disclosure.append(toggleButton);

				// TEALEAF SESSION TRACKING
				if (typeof(TeaLeaf) != "undefined" &&  typeof(TeaLeaf.Client) != "undefined") {
					TeaLeaf.Client.tlProcessNode('document.body');
				}
				
				toggleButton.bind("click", function(event) {
					
					if (disclosure.is(".disclosureClosed")) {
						//Open it	
						disclosure.find(".disclosureContent:first").slideDown(options.speed, function(){
							if(typeof(options.openCallback) == "function"){
								options.openCallback(event);
							}
							disclosure.removeClass("disclosureClosed").addClass("disclosureOpen");
							
							//If disclosure is in a modal then resize to accomidate
							if(Sprint.modal.elem && Sprint.modal.elem.length > 0){
								Sprint.modal.elem.sizeModal({reposition:false});
							}
						});
						
						$(this).html(options.openedText);
						
					}
					else {
						//Close	it
						disclosure.find(".disclosureContent:first").slideUp(options.speed, function(){
							if(typeof(options.closeCallback) == "function"){
								options.closeCallback(event);
							}
							disclosure.removeClass("disclosureOpen").addClass("disclosureClosed");
							
							//If disclosure is in a modal then resize to accomidate
							if(Sprint.modal.elem && Sprint.modal.elem.length > 0){
								Sprint.modal.elem.sizeModal({reposition:false});
							}
						});
						
						$(this).html(options.closedText);
					}
					
					// TEALEAF SESSION TRACKING
					if (typeof(TeaLeaf) != "undefined" &&  typeof(TeaLeaf.Client) != "undefined") {
						TeaLeaf.Client.tlAddEvent(event);
					}
					
					return false;
				});
				
				if (options.titleClickable) {
					//Let users click on the heading element to open/close the disclosure
					disclosure.find(".disclosureTitle:first").bind("click", function() {
						disclosure.find(".disclosureToggle:first").trigger("click");
					}).css("cursor", "pointer");
				}
				
			});
		
		},

		/*
		* 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;
			
		},
		
		/*
		* equalizeWidths
		*
		* function to equalize the widths of a set of elements
		*
		*/
		equalizeWidths: function() {
		
			var finalWidth = 0;
			
			$(this).each(function() {
				
				if ($(this).outerWidth() > finalWidth) {
					finalWidth = $(this).outerWidth();
				}
				
			});
			
			$(this).each(function() {
				$(this).width(finalWidth - ($(this).outerWidth() - $(this).width()));
			});
			
			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;

							});
						}
						
						//Copy tabindex attribute
						if(parseInt(theButton.attr("tabindex")) > 0){
							newAnchor.attr("tabindex",theButton.attr("tabindex"));
						}
						
						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
		* @param appendAfter: Boolean: If true, will append the shim IFRAME right *after* the target element.
		*/
		addShim: function(zIndex, appendAfter) {
		
			//Only run this function for IE6-
			if ($.browser.msie && $.browser.getIEversion() < 7) {
			
				return this.each(function() {
					// default (no value) is false
					appendAfter = appendAfter === true;
					
					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 = el.data("shim") != null;
					
					if (!hasShim) {
						var shimID = ++Sprint.ieShims.currentShims; //Increment the number of shims (to generate unique ids)
						
						//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 = {
							pos: appendAfter == true ? el.position() : el.offset(),
							width: el.outerWidth(),
							height: el.outerHeight()
						};
						
						var newShim = $("<iframe src=\"javascript:false;\" id=\"IESHIM_"+shimID+"\"></iframe>").css({
							position:  "absolute",
							top:       props.pos.top+"px",
							left:      props.pos.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);
						}
						
						if (appendAfter == true) {
							el.after(newShim);
						}
						else {
							newShim.appendTo("body");
						}
						
						el.data("shim", newShim);
					}
				});
			}

			return this;
		},
		
		
		/*
		* removeShim
		*
		* function to remove the iframe shim
		*
		*/
		removeShim: function() {
			//Only run this function for IE6-
			if ($.browser.msie && $.browser.getIEversion() < 7) {
				return this.each(function() {
					var el = $(this);
					
					//Find the shim for this item in the Sprint.ieShims.elem array
					var targetShim = el.data("shim");
					
					//Remove the shim
					if (targetShim) {
						targetShim.remove();
						el.data("shim", null);
					}
				});
			}
			
			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.fn.getContentString("carousel.nextButtonText"),     //string: the text for the "Next" link
				prevButtonText:  Sprint.fn.getContentString("carousel.prevButtonText"),  //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 {
				
					// Old Value: var carouselList = $(this).find("ul");
					// Modified to accomodate nested List Items within the Carousel
					var carouselList = $(this).find("ul").not("ul li ul");
						
                    			//Changed for eCenter, items could contain nested Lists so look for only top level parents to avoid whitespace.
					// var numberOfItems = carouselList.find("li").length;
					var numberOfItems = carouselList.find("li").not("li ul 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");
					}
					
					//Determines whether there is 3 or less phones and hides the previous and next buttons
					if(options.type == 'list'){
						var numOfButtons = $('.carouselClip li');
						if(numOfButtons.length <= 3) {
							prevLink.css({'visibility':'hidden'});
							nextLink.css({'visibility':'hidden'});
						}
					}
				}
				
				//Setup the event handlers for the prev/next links
				prevLink.bind("click", function(event) {
					
					// TEALEAF SESSION TRACKING
					if (typeof(TeaLeaf) != "undefined" &&  typeof(TeaLeaf.Client) != "undefined") {
						TeaLeaf.Client.tlAddEvent(event);
					}

					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(event) {
					
					// TEALEAF SESSION TRACKING
					if (typeof(TeaLeaf) != "undefined" &&  typeof(TeaLeaf.Client) != "undefined") {
						TeaLeaf.Client.tlAddEvent(event);
					}

					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();

			});
		},
		
		/*
		* showFormSuggestions
		*
		* function to display any form suggestions that may have occured
		*
		* @param scriptOptions: Object, defines options that will override the defaults
		*
		*/
		showFormSuggestions: function(scriptOptions) {
			
			var defaults = {
				showInline:			true, // boolean: set this value to false if the form suggestions should NOT appear after each field
				showSummary:		true, // boolean: set this value to false if you don't want to display a suggestion summary at the beginning of the form
				summaryAnchor:		null, // jQuery object: the object to append the suggestion summary to
				suggestionData:		null, // object: the suggestion data, includes all the form fields that have suggestions and their error messages
				suggestionSummaryMessage:	"",	  // string: the suggestion summary message to show at the beginning of the form
				scrollToSummary:	true, // boolean: set this value to false if you don't want the page to auto-scroll to the suggestion summary
				callback:			null  // function: a function that will be run after the form suggestions are shown
			};
			
			var options = $.extend(defaults, scriptOptions);
			
			return this.each(function() {
				
				var currentForm = $(this);
				var numOfSuggestions = 0;
				
				if (options.showSummary) {
					var suggestionSummary = $("<ul class=\"formSuggestions\"></ul>");
				}
				
				// loop through the suggestion data
				for (field in options.suggestionData) {
					var currentField = currentForm.find("[name='"+options.suggestionData[field].name+"']");
					var currentSuggestionMessage = options.suggestionData[field].suggestionMessage;
					
					currentField.addClass("suggestion");
					
					if (currentField.length < 1) break;
					numOfSuggestions++;
					
					//Add inline suggestion message if it's needed
					if (options.showInline) {
					
						//Create the suggestion message
						var inlineSuggestionMessage = $("<label for=\""+currentField.attr("id")+"\" class=\"suggestion\">"+currentSuggestionMessage+"</label>");

						//Check to see if the current field is inside a label
						if (currentField.parent("label").length > 0) {
							inlineSuggestionMessage.appendTo(currentField.parent().parent());
						}
						else {
							inlineSuggestionMessage.insertAfter(currentField);
						}
					}
					
					if (options.showSummary && options.summaryAnchor != undefined && numOfSuggestions > 0) {
						if (options.scrollToSummary) {
							options.summaryAnchor.scrollTo({speed: "slow"});
						}
						options.summaryAnchor.append(suggestionSummary);
					}
					
					// Change the value of the current field to the suggested value
					currentField.val(options.suggestionData[field].suggestedValue);
				
					//If there's a callback function to be run after showing suggestions, run it now
					if (options.callback) {
						options.callback();
					}
					
				} // end looping through suggestion data
				
				// Add the suggestion summary (if suggestion summary is wanted)
				if (options.showSummary) {						
					$("<li>"+options.suggestionSummaryMessage+"</li>").appendTo(suggestionSummary);
				}
				
			});
		},				
		
		/*
		* 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
				showSummaryPlain: false, //boolean: set this value to true if you want the error message to be shown the same as the inline errors
				showErrorLabel: true,    //Default it is true, if it false we will not show errorlabel if any error messsages.
				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
				scrollToFirstError:  false,  //boolean: set this value to true to auto-scroll to the first error
				callback:         null   //function: a function that will be run after the form errors are shown
			};
			
			var options = $.extend(defaults, scriptOptions);
			
			return this.each(function() {
			
				var currentForm = $(this);
				var numOfErrors = 0;
				var omnitureErrorString = '';
				
				if (options.showSummary) {
					var errorSummary = $("<ul class=\"formErrors\"><li></li></ul>");
					var errorSummaryListItem = errorSummary.find("li");
				}

				//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;
					
					//Don't add class="error" to <select> controls, it causes them not to work in Firefox.
					if (!currentField.is("select")) {
						currentField.addClass("error");
					}
					
					var additionalFieldsLength = 0;
					
					if (options.errorData[field].additionalFields) {
						additionalFieldsLength = options.errorData[field].additionalFields.length;
					}

					//If additional field names are defined, add a class of "error" to each one.					
					if (additionalFieldsLength) {
						for (var x = 0; x < additionalFieldsLength; x++) {
							currentForm.find("[name='"+options.errorData[field].additionalFields[x]+"']").filter(":not('select')").addClass("error");
						}
					}
					
					// if current field is not found on the page then continue onto the next field in the error data
					if (currentField.length < 1) continue;
					numOfErrors++;
					
					//Add the error to the error summary (if error summary is wanted)
					if (options.showSummary) {
						var itemLabel = currentForm.find("label[for='"+currentField.attr("id")+"']");
						var itemLabelId = itemLabel.attr("id");
						var itemLabelText = itemLabel.text().replace("*", "").replace(":", "");
						
						if (options.errorData[field].fieldLabel) {
							itemLabelText = options.errorData[field].fieldLabel;
						}
						
						if (!itemLabelId) {
							//If the label doesn't have an ID to link to, use the field ID instead
							itemLabelId = currentField.attr("id");
						}
						
						//If showInline is set to false and showSummary is set to true, show the full error messages after each field name in brackets.
						if (!options.showInline && options.showSummary) {
							//If we are showing a plain summary, no need to insert the error into brackets
							if (options.showSummaryPlain) {
								itemLabelText += currentErrorMessage;
							}
							else {
								if(!options.showErrorLabel){
									itemLabelText = "";
									itemLabelText += " "+currentErrorMessage;
								} else {
								itemLabelText += " ("+currentErrorMessage+")";
								}
							}
						}
						
						if (numOfErrors == 1) {
							$("<span> <a href=\"#"+itemLabelId+"\">"+itemLabelText+"</a></span>").appendTo(errorSummaryListItem);
						}
						else {
							$("<span>, <a href=\"#"+itemLabelId+"\">"+itemLabelText+"</a></span>").appendTo(errorSummaryListItem);
						}
							
					}
					
					// Accumulate error messages to be passed to omniture
					omnitureErrorString += (omnitureErrorString == '' ? '' : '|') + currentErrorMessage;
					
					//Add inline error message if it's needed
					if (options.showInline) {
						
						
						if(typeof(options.errorData[field].multipleErrors) == "object"){
							
							//show multiple errors for a single input
							var inlineErrorMessage = $('<div class="multipleErrors"></div>');
							for (i in options.errorData[field].multipleErrors) {
								var errorMessageObj = $('<label id="' + i + '" for="' + currentField.attr("id") + '" class="error"></label>');
								errorMessageObj.html(options.errorData[field].multipleErrors[i].errorMessage);
								inlineErrorMessage.append(errorMessageObj);
							}
							
							
						} else {
							
							// Create the error message. If there's html tags in the error message, use appendTo, otherwise do it the same way as before.
							// This is being done because when there's block level elements in an error message, the message gets added after the label
							// and not inside it.
							var inlineErrorMessage = $("<label id=\"lblInlineError"+ currentField.attr("id") + "\" for=\""+currentField.attr("id")+"\" class=\"error\"></label>");
							inlineErrorMessage.html(currentErrorMessage);
						}
						
						//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 there's a complex summary, prepend the summary of error fields with the appropriate message (based on 1 or n error messages)
				if (options.showSummary && !options.showSummaryPlain) {

					var errorSummaryTextLabel;

					if (numOfErrors == 1) {
						errorSummaryTextLabel = Sprint.fn.getContentString("formFieldErrors.errorLabelSingle");
					}
					else {
						errorSummaryTextLabel = Sprint.fn.getContentString("formFieldErrors.errorLabel");
					}
					
					if(options.showErrorLabel){
						errorSummaryListItem.prepend("<span>"+errorSummaryTextLabel.replace("<$>", numOfErrors)+"</span>");
					} 

				}				
				
				if (options.showSummary && options.summaryAnchor != undefined && numOfErrors > 0) {
					if (options.scrollToSummary) {
						options.summaryAnchor.scrollTo({speed: "slow"});
					}
					options.summaryAnchor.append(errorSummary);
				}
				
				// If there is a smooth scroll to first error message, do that now
				if (options.scrollToFirstError) {
					var firstError = $("label.error:first");
					var firstErrorFor = $("label.error:first").attr("for");
					// check to see if there is a summary anchor, we don't want to scroll twice
					if (options.summaryAnchor == undefined && firstError.length > 0){
							firstError.scrollTo({speed:"slow",topOffset:30}); //Increased offset value so that the user is able to see the error if it appears above the field. 
							//the first SSN text field doesn't match it's label, unlike the phone numbers
							if (firstErrorFor == "txtSSN"){
								$("#"+firstErrorFor+"val0").focus();
							} else {
								//check to see if this is a password confirmation box, if it is, don't focus on it
								if( $("#"+firstErrorFor).attr("id").indexOf("PasswordConfirm") == -1){
									$("#"+firstErrorFor).focus();
								}
							}
					}
				}
				
				//If there's a callback function to be run after showing errors, run it now
				if (options.callback) {
					options.callback();
				}
				
				if (numOfErrors > 0) {
					 //TECH IMPLEMENTATION
					var omnitureCall1 = $('#OmnitureCalls');
					var omnitureCall2 = $('#disableOmnitureErrorTrac');
					var appIdType = "";
					
					if (omnitureCall1.length > 0 && omnitureCall1.val() == 'false') {
						appIdType = "Support";
					} else if(omnitureCall2.length > 0 && omnitureCall2.val() == 'false'){
						if(Analytics.eComATG) {
							appIdType = "eComATG";
						} else if(Analytics.eCareATG) {
							appIdType = "eCareATG";
						}
					}
					if(appIdType != "") {
						try {
							Analytics[appIdType].trackError('user', omnitureErrorString);
						} catch(e){};
					}
				}
			
			});
		
		},



		/*
		* 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",  //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);
			}
			*/
			
			var firstEl;
			
			//prevent select boxes from being selected when modal opens.
			if ($.browser.msie && $.browser.getIEversion() < 7){
				for (var i=0; i<focusableElements.length; i++) {
					if(!focusableElements.eq(i).is("select")){
						firstEl = focusableElements.eq(i);
						break;
					}
				}
			} else {
				firstEl = focusableElements.eq(0);
			}
			
			if(firstEl != undefined){
				setTimeout(function() {
					firstEl.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 = {
				sorting:			   true,   //boolean: set this value to false to turn off sorting click events on table headers
				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
				ajaxPagination:		   false,   //boolean: set this value to true if more results will be loaded into the table with ajax
				ajaxPaginationCallback: false,  //boolean: set this value true if checkbox selection need after results will be loaded with ajax.
				ajaxOptions:		   {},   	//object: override options for the ajax call. url is required.
				ajaxJsonOPtions:	   null,   //object: set this value true if more results will be loaded into the table with JSON.
				ajaxPageUpdate:		   null,   //function: a function that can be used to add data to the table
				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");
				
				if(options.ajaxPagination){
					currentTable.data("ajaxPaginationComplete", false);
				}
				
				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, resetPages, movingLeft) {
					
					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:first > tr").not('tr.amdocHeader');
					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 paginationLists = $("div.tablePagination");
						
						//Hide page numbers if we have more than 6 (show elipses instead)
						if (totalPages > 6) {
						
							paginationLists.each(function(i) {
							
								var currentPageList = $(this);
								var elipsisUp = currentPageList.find("li.elipsis.up");
								var elipsisDown = currentPageList.find("li.elipsis.down");
								var lastPage = currentPageList.find("li.last");

								var numberedListItems = currentPageList.find("li").not(".pagePrev, .pageNext, .elipsis");

								//If the page number is already visible, do nothing*
								if (resetPages && pageNumber != 1 && pageNumber != totalPages && numberedListItems.eq(parseInt(pageNumber) - 1).is(":visible") && !currentTable.data("updatePageNums")) {
									return;
								}

								//Hide all the numbered items to begin
								numberedListItems.hide();
								
								//Now, figure out which pages to show, & which elipses links to show
								if (pageNumber < 6) {
									//Show the first 5 links, the last link, and the "up" elipsis
									numberedListItems.filter(":lt(5)").show();
									elipsisUp.show().find("a").attr("href", "?p=6");
									
									//Hide the down elipsis
									elipsisDown.hide();
								}
								else {
									//Show "down" elipsis and then figure out which 5 page numbers to show and whether or not we need to also show the "up" elipsis.
									elipsisDown.show();
									
									//If we're moving left through the pagination, than the next page should be at the end of the list
									if (movingLeft) {
										if (parseInt(pageNumber) < totalPages) {
											//Show the "up" elipsis because there are more pages...
											elipsisUp.show();
											numberedListItems.filter(":lt("+pageNumber+")").show();
											
											//Now hide some
											numberedListItems.filter(":lt("+(parseInt(pageNumber) - 5)+")").hide();
										}
										else {
											//Hide the up elipsis
											elipsisUp.hide();
											
											//Show the last 5 pages
											numberedListItems.filter(":gt("+(totalPages - 6)+")").show();
										}
										if(options.ajaxPagination){
											if(currentTable.data("ajaxPaginationComplete") && lastPage.is(":hidden") && elipsisDown.is(":hidden")){
												elipsisDown.show();
												currentPageList.find("li.pageNext").removeClass("disabled");
											}
										}
									
									}
									else {
										if (parseInt(pageNumber) + 4 < totalPages) {
											//Show the "up" elipsis because there are more pages...
											elipsisUp.show();
											numberedListItems.filter(":gt("+(parseInt(pageNumber) - 2)+")").show();
											
											//Now hide some
											numberedListItems.filter(":gt("+(parseInt(pageNumber) - 1 + 4)+")").hide();
										}
										else {
											//Hide the up elipsis
												
											if(!options.ajaxPagination){
												elipsisUp.hide();
											}
											
											//Show the last 5 pages
											numberedListItems.filter(":gt("+(totalPages - 6)+")").show();
											
											if(options.ajaxPagination){
												if(currentTable.data("ajaxPaginationComplete") && lastPage.is(":visible")){
													elipsisUp.hide();
													currentPageList.find("li.pageNext a").addClass("disabled");
												}
											}
										}
									}
								}

								//Show the first & last page numbers
								if(!options.ajaxPagination){
									numberedListItems.filter(".first, .last").show();
								}	
								
								//Figure out what the href values of the elipses should be
								var lastVisibleItemAnchor = numberedListItems.not(".first, .last").filter(":visible").filter(":last").find("a");
								var firstVisibleItemAnchor = numberedListItems.not(".first, .last").filter(":visible").filter(":first").find("a");
								
								var upElipsisPageNumber = parseInt(Sprint.fn.getURLParameter(Sprint.fn.getQueryString(lastVisibleItemAnchor.attr("href")), "p")) + 1;
								var downElipsisPageNumber = parseInt(Sprint.fn.getURLParameter(Sprint.fn.getQueryString(firstVisibleItemAnchor.attr("href")), "p")) - 1;
								
								elipsisUp.find("a").attr("href", "?p="+upElipsisPageNumber);
								elipsisDown.find("a").attr("href", "?p="+downElipsisPageNumber);

							});
						}
					
						var paginationLinks = paginationLists.find("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")) {
									
									if(!options.ajaxPagination){
										currentLink.addClass("selected");
									} else if (options.ajaxPagination && !currentLink.parent().is(".elipsis.up")){
										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 {
										if(!options.ajaxPagination){
											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();
							}
						}
					}
				}
				
				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:first > 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 there are more than 6 pages, elipses will be needed
							if (totalPages > 6 && !options.ajaxPagination) {
								//Add in ellipsis markers inside first and last pages
								$("<li class=\"elipsis down\"><a href=\"#\">&hellip;</a>").insertAfter(paginationControls.find("li.first"));
								$("<li class=\"elipsis up\"><a href=\"#\">&hellip;</a>").insertBefore(paginationControls.find("li.last"));
							} else if (totalPages > 6 && options.ajaxPagination) {
								//Add in ellipsis markers outside first and last pages
								$("<li class=\"elipsis down\"><a href=\"#\">&hellip;</a>").insertBefore(paginationControls.find("li.first"));
								$("<li class=\"elipsis up\"><a href=\"#\">&hellip;</a>").insertAfter(paginationControls.find("li.last"));
							}
						}
						
						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 link is disabled then return
							if(anchor.is(".disabled")){
								return;
							}
							
							if (anchor.is("a") && !anchor.is(".selected")) {
							
								var currentHref = anchor.attr("href");
								var movingLeft = false;
								
								//Strip off everything before the ? character
								currentHref = currentHref.substr(currentHref.indexOf("?"));
								var pageToShow = Sprint.fn.getURLParameter(currentHref, "p");
								
								if (anchor.parent().is(".pagePrev") || anchor.parent().is(".elipsis.down")) {
									movingLeft = true;
								}
								
								if(options.ajaxPagination){
									//Start: this is only for checkbox selection on page level for 2874 req
									var lastPage = currentTable.parent().find("div.tablePagination:first li.last");
									var selAllChkboxNo = '';
									// Only trigger ajax calls if we're on the last page
									if(lastPage.is(":visible") && anchor.parent().is(".elipsis.up") || lastPage.find("a").is(".selected") && anchor.parent().is(".pageNext")){
										selAllChkboxNo = parseInt(Sprint.fn.getURLParameter(currentHref, "p")) + 1;
									}else {
										selAllChkboxNo = parseInt(Sprint.fn.getURLParameter(currentHref, "p"));
									}
									if(options.ajaxPaginationCallback){
										var selALlChkId = $("thead th input#ac_selectAll"+selAllChkboxNo+"").length;
										if(selALlChkId == 0){
											$("thead th input[type='checkbox']").hide();
											$("thead th.selectSubscriber a").after('<input type="checkbox" id="ac_selectAll'+selAllChkboxNo+'" name="ac_selectAll" class="chkAllPageSubs"/>').show();
											
											
											$("#subscriberStatusTableHeader input[type='checkbox']:visible").bind("click",function(){
												var isChecked = $(this).is(':checked');
												if(isChecked){
													$("#subscriberStatusTableBody input[type='checkbox']:visible").not(':disabled').attr("checked","checked");
													if($("a#btnBulkEdit").hasClass('disabled')){
														$("a#btnBulkEdit").removeClass('disabled');
													}
												} else{
													$("#subscriberStatusTableBody input[type='checkbox']:visible").not(':disabled').removeAttr("checked");
													var chkLen = $("#subscriberStatusTableBody input[type='checkbox']:checked").length;
													if($("a#btnBulkEdit").hasClass('button1_converted') && chkLen <= 0){
														$("a#btnBulkEdit").addClass('disabled');
													}

												}
											});
											
											
										} else {
											$("thead th input[type='checkbox']").hide();
											$("thead th input#ac_selectAll"+selAllChkboxNo+"").show();
											
										}
										//select all BAn check box is checked or not
										if($("#chkAllBan input[type='checkbox']:checked").length > 0){
											$("#subscriberStatusTable input[type='checkbox']").removeAttr("checked");
											$("#subscriberStatusTable input[type='checkbox']").attr("disabled","disabled");
										}
									}
									//End:End for the checkbox selection on page level for 2874 req
									if (anchor.parent().is(".pageNext") || anchor.parent().is(".elipsis.up")) {
										var lastPage = currentTable.parent().find("div.tablePagination:first li.last");
										// Only trigger ajax calls if we're on the last page
										if(lastPage.is(":visible") && anchor.parent().is(".elipsis.up") || lastPage.find("a").is(".selected") && anchor.parent().is(".pageNext")){
											
											var ajaxDefaults = {
												data: "",
												dataType: "html",
												type: "GET",
												url: "/global/mysprint/mypreferences/ajax/accountcontrols_subs.php",
												async: false,
												success: function(tableData){
													// tableData var is available for use in the options.ajaxPageUpdate callback
													if(options.ajaxPaginationCallback){
														currentTable.find("tbody").append(tableData);
													}
													if(options.ajaxJsonOPtions){
														
														//Sprint.fn.getBulkAccount(currentTable.parent(), tableData);
														options.ajaxJsonOPtions(currentTable.parent(), tableData);
														
													}
													setupPagination();
													
													currentHref = lastPage.find("a").attr("href");
													currentHref = currentHref.substr(currentHref.indexOf("?"));
													pageToShow = parseInt(Sprint.fn.getURLParameter(currentHref, "p")) + 1;
													currentTable.data("updatePageNums", true);
													
													if(options.ajaxPageUpdate){
														options.ajaxPageUpdate(currentTable);
													}
													if(options.ajaxPaginationCallback){
														//select all BAn check box is checked or not
														if($("#chkAllBan input[type='checkbox']:checked").length > 0){
															$("#subscriberStatusTable input[type='checkbox']").removeAttr("checked");
															$("#subscriberStatusTable input[type='checkbox']").attr("disabled","disabled");
														}
														var hidSubCheckList = $("input#hid_subCheckedlist").val();
														var chkLen = $("#subscriberListWrapper input[type='checkbox']:checked").length;
														if(hidSubCheckList == "selectAllOnBan") {
															$("#subscriberListModal  #subscriberListWrapper #chkAllBan input[type='checkbox']").attr('checked','checked');
															$("#subscriberStatusTable input[type='checkbox']").removeAttr("checked").attr('disabled','disabled');
														}else if(hidSubCheckList != ''){
															var checkedListArray = hidSubCheckList.split(",");
															$("div#subscriberListWrapper #subscriberStatusTable tr.ac_subscriber td input[type='checkbox']").each(function(i){
																var thisVal = $.inArray($(this).val(), checkedListArray);
																if(thisVal > -1){
																	$(this).attr('checked','checked');
																}
															});
																
															//var isAllChkChecked = $("#subscriberStatusTableBody input[name='selectSubscriber']:visible").not(':disabled').is(':checked');
															var isAllChkChecked = $("#subscriberStatusTableBody input[name='selectSubscriber']:visible").not(':disabled').filter(function(i){
																if($(this).is(':checked')){
																	return true;
																}
															});
															var chksPerPage = $("#subscriberStatusTableBody input[name='selectSubscriber']:visible").not(':disabled').length;
															if((isAllChkChecked.length == chksPerPage) && (chksPerPage > 0)){
																$("#subscriberStatusTableHeader input[type='checkbox']:visible").attr('checked','checked');
															}

														}
														if((chkLen > 0) || (hidSubCheckList != '')){
															$("a#btnBulkEdit").removeClass("disabled");
														}
														//if disabled the all scribers in the page we are disabling the select all on page checkbox
													/*	var isAllChkDisabled = $("#subscriberStatusTableBody input[type='checkbox']:visible").filter(function(i){
															if($(this).is(':disabled')){
																return true;
															}
														});
														var chksDisPerPage = $("#subscriberStatusTableBody input[name='selectSubscriber']:visible").length;
														if((isAllChkDisabled.length == chksDisPerPage) && (chksDisPerPage > 0)){
															$("#subscriberStatusTableHeader input[type='checkbox']:visible").attr('disabled','disabled');
														}
														*/
														$("p.paginationText").remove();
														$("#subscriberListWrapper .tablePaginationAbove").after('<p class="paginationText">Selected subscribers will be saved as you move from page to page.</p>');
														$("#subscriberListWrapper .tablePaginationBelow").before('<p class="paginationText">Selected subscribers will be saved as you move from page to page.</p>');
														
													}
												},
												error: Sprint.fn.ajaxError
											};
											var ajaxOptions = $.extend(ajaxDefaults, options.ajaxOptions);
											
											$.ajax(ajaxOptions);
										}
									}
								}
								switchToPage(pageToShow, true, movingLeft);
								if($("#subscriberListWrapper").length >0){
									$("#subscriberListWrapper").scrollTo();
								}
								if(options.ajaxPaginationCallback){
								   //if checked select all on page checkbox when all subscribers checked on the page
									var isAllChkChecked = $("#subscriberStatusTableBody input[name='selectSubscriber']:visible").not(':disabled').filter(function(i){
										if($(this).is(':checked')){
											return true;
										}
									});
									var chksPerPage = $("#subscriberStatusTableBody input[name='selectSubscriber']:visible").not(':disabled').length;
									if((isAllChkChecked.length == chksPerPage) && (chksPerPage > 0)){
										$("#subscriberStatusTableHeader input[type='checkbox']:visible").not(':disabled').attr('checked','checked');
									}
									//if disabled the all scribers in the page we are disabling the select all on page checkbox
									/*var isAllChkDisabled = $("#subscriberStatusTableBody input[type='checkbox']:visible").filter(function(i){
										if($(this).is(':disabled')){
											return true;
										}
									});
									var chksDisPerPage = $("#subscriberStatusTableBody input[name='selectSubscriber']:visible").length;
									if((isAllChkDisabled.length == chksDisPerPage) && (chksDisPerPage > 0)){

										$("#subscriberStatusTableHeader input[type='checkbox']:visible").attr('disabled','disabled');
									}*/
								}
							}
							
						});
						
						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);
				}
				
				//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 {
							if(options.sorting){
								currentTH.find("a").unbind("click"); //If there's already an anchor tag, unbind it's current click event.
							}
						}
					}
					
				});
				
				function sortTableByColumn(columnIndex) {
				
					var selectedTH = currentTable.find("thead th").eq(columnIndex);
				
					var tbody = currentTable.find("tbody");
					var oldRows = currentTable.find("tbody:first > 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:first > 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();
					}
				
				}
				
				if(options.sorting){
					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);
				
				/* Briefly show .tabContent layers before the tabbedContent components get rendered. This is to ensure that the parent .tabContent layer has display: block; set so that child components that need to report a width/height in order to properly setup their components (such as sIFR text) will report a number and not just 0.
				----------------------------------------------------*/
				baseObject.find("div.tabContent").show();
				
				/* 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, .tooltipBox").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 && $.browser.getIEversion() == 6 && $.browser.msie) {
					baseObject.find(".pngFix").ifixpng();
				}
			
			});
		
		},
		
		/*
		* makeAbsolute
		*
		* function to position elements as absolute based on their current relative position
		*
		* @param scriptOptions: Object, defines options that will override the defaults
		*
		*/
		makeAbsolute: function(scriptOptions) {
			
			var defaults = {
				
				vSnap: 	"top",   //boolean: set this value to false if you don't want to setup sIFR headings
				hSnap:  "left"    //boolean: set this value to false if you don't want to setup 5-star Rating Systems

			};

			var options = $.extend(defaults, scriptOptions);
			
		    return this.each(function() {

		        var targets = $(this);
		        var pos = targets.position();
		
				var vSnap = options.vSnap == "bottom" ? "bottom" : "top";
				var hSnap = options.hSnap == "right" ? "right" : "left";
							
				if (vSnap == "bottom") {
					if (hSnap == "right") {
						targets.css({position:"absolute", marginLeft: 0, marginTop: 0, top:"auto", bottom: -pos.top, left:"auto", right: -pos.left});
					} 
					else 
					{
						targets.css({position:"absolute", marginLeft: 0, marginTop: 0, top:"auto", bottom: -pos.top, left: pos.left, right:"auto"});
					}
				}
				else
				{
					if (hSnap == "right") {
						targets.css({position:"absolute", marginLeft: 0, marginTop: 0, top: pos.top, bottom:"auto", left:"auto", right: -pos.left});
					} 
					else 
					{
						targets.css({position:"absolute", marginLeft: 0, marginTop: 0, top: pos.top, bottom:"auto", left: pos.left, right:"auto"});
					}
				}
		
		    });
		},
		
		paginator: function(scriptOptions) {
			var defaults = {
				current:     1,      //integer: currently active page
				totalPages:  5,      //integer: number of pages to render
				numbers:     true,   //boolean: set this value to false if you don't want numbers in your pagination controls
				nextPrev:    true    //boolean: set this value to false if you don't want the Next/Previous buttons to show in your pagination controls
			};
			
			var options = $.extend(defaults, scriptOptions);

			return this.each(function() {
				if (options.totalPages <= 1) {
					return;
				}
				
				// create pagination controls
				var html = '<ul class="pageList">',
					from = 1,
					to = from + options.totalPages
					;
			
				if (options.nextPrev) {
					html += "<li class='pagePrev'><a href='#' class='prev'>Previous</a></li>";
				}
				
				// for each page, create a numbered link (if required)
				if (options.numbers) {
					for (var i = from; i < to; i++) {
						html += "<li class='number'><a href='?p="+i+"'>"+i+"</a>";
					}
				}
			
				if (options.nextPrev) {
					html += "<li class='pageNext'><a href='#' class='next'>Next</a></li>";
				}
			
				html += "</ul>";
			
				var container = $(this),
					paginator = $(html),
					numbers = paginator.find("li.number")
					;
					
				container.html(paginator);
				
				numbers.eq(0).addClass("first");
				numbers.filter(":last").addClass("last");
				
				function setActivePage(index, triggerEvent) {
					numbers
						.removeClass("active")
						.eq(index - 1).addClass("active")
						;
					
					function disable(target, expr) {
						target[expr ? "addClass" : "removeClass"]("disabled");
					};
					
					disable(paginator.find(".pagePrev a"), index <= 1);
					disable(paginator.find(".pageNext a"), index >= options.totalPages);
					
					if (triggerEvent != false) {
						$(paginator).trigger("pagechange", index);
					}
				};
				
				function getPage(a) {
					try {
						var queryString = /\?(.*)$/.exec($(a).attr("href"))[0];
						return parseInt(Sprint.fn.getURLParameter(queryString, "p"));
					}
					catch(e) {
						return NaN;
					}
				};
				
				setActivePage(options.current, false /* don't trigger the first event */);
				
				// handle the previous page arrow
				paginator.find(".pagePrev").unbind().bind("click", function(e) {
					e.preventDefault();
					var page = getPage(paginator.find("li.active").prev(".number").find("a"));
					
					if (!isNaN(page) && page > 0) {
						setActivePage(page);
					}
				});
				
				// handle the next page arrow
				paginator.find(".pageNext").unbind().bind("click", function(e) {
					e.preventDefault();
					var page = getPage(paginator.find("li.active").next(".number").find("a"));
					
					if (!isNaN(page) && page <= options.totalPages) {
						setActivePage(page);
					}
				});
				
				// handle 
				numbers.unbind().find("a").bind("click", function(e) {
					e.preventDefault();
					setActivePage(getPage(this));
				});
			});
		}
		
	});

	// Extend jQuery to use native js array reverse() function.
	$.fn.reverse = [].reverse;

	//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
	
	// based on 
	// http://devongovett.wordpress.com/2009/04/06/text-overflow-ellipsis-for-firefox-via-jquery/
	// use forcedWidth argument for long unbroken strings of text in tables. In IE tables will resize to fix text, ignoring width attributes 
	$.fn.ellipsis = function(scriptOptions) {
		var defaults = {
			forcedWidth:null,	//int: set a static width
			showTitle:  false	//boolean: put the original text into the elements title attribute to be shown on hover
		};
		var options = $.extend(defaults, scriptOptions);
		
		return this.each(function()
		{
			var el = $(this);

			// AlexGorbatchev : extended to support multiple lines
			if(el.css("overflow") == "hidden")
			{
				var text = el.html();
				var elWidth = options.forcedWidth || el.width();
				var multiline = el.hasClass('multiline');
				
				var t = el.clone();
					t.css({position:'absolute',overflow:'visible'})
					 .width(multiline ? elWidth : 'auto')
					 .height(multiline ? 'auto' : el.height())
					 .hide;
					 
				el.before(t);
				
				function height() { return t.height() > el.height(); };
				function width() { return t.width() > elWidth; };

				var func = multiline ? height : width;
				
				if (!multiline && text.length > 0 && func()){
					el.attr('title', text);
				}
				else if(multiline && options.showTitle){
					el.attr("title",text);
				}
				
				while (text.length > 0 && func()){
					text = text.substr(0, text.length - 1);
					t.html(text + "...");
				}

				el.html(t.html());
				t.remove();
			}
		});
	};
	
	// Autotabbing
	// E.g. $( '#field1, #field2', '#field3' ).autotab();
	// Requires maxlength to be set on autotabbed fields
	// Field IDs must be set in order per tabbing group
	jQuery.fn.autotab = function( type )
	{
		var fieldTypes = { any:true, number:true, alpha:true, alphanum:true };
		var fieldType = arguments.length > 0 && fieldTypes[ type ] ? type : 'any';

		if ( this.length < 2 ) return this;

		var fields = jQuery.makeArray( this.slice() );
		var keyCodeFilter = function(k, s, a)
		{
			if ( fieldType == 'any' ) return k > 47 || k == 32;
			else if ( fieldType == 'number' ) return ( ( k > 47 && k < 58 && !s ) || ( k > 95 && k < 106 ) ) && !a;
			else if ( fieldType == 'alpha' ) return k > 64 && k < 91 && !a;
			else if ( fieldType == 'alphanum' ) return ( ( k > 47 && k < 58 && !s ) || ( k > 95 && k < 106 ) || ( k > 64 && k < 91 ) ) && !a;
		};

		jQuery.each( fields, function(i)
		{
			var maxlength = jQuery( this ).attr( 'maxlength' );
		
			// check it's not the last field and it has a maxlength
			if ( i < fields.length - 1 && maxlength )
			{
				maxlength = parseInt( maxlength, 10 );
			
				jQuery( this ).bind( 'keyup', function(e)
				{
					// check for proper characters
					if ( keyCodeFilter( e.keyCode, e.shiftKey, e.altKey ) )
					{
						// check field is full
						if ( jQuery( this ).val().length >= maxlength )
						{
							// focus next field
							jQuery( this ).trigger( 'blur' );
							jQuery( fields[ i + 1 ] ).focus().select();
						}
					}
				} );
			}
			
			// field type validation
			if ( fieldType != 'any' )
			{
				jQuery( this ).bind( 'keydown', function(e)
				{
					jQuery( this ).data( 'keycode', e.keyCode );
					jQuery( this ).data( 'shiftkey', e.shiftKey ? 'true' : 'false' );
					jQuery( this ).data( 'altkey', e.altKey ? 'true' : 'false' );
				} )
				.bind( 'keypress', function(e)
				{
					var k = jQuery( this ).data( 'keycode' );
					var s = jQuery( this ).data( 'shiftkey' ) == 'true' ? true : false;
					var a = jQuery( this ).data( 'altkey' ) == 'true' ? true : false;

					// check for proper characters
					if ( ( k > 47 && !keyCodeFilter( k, s, a ) ) ||  k == 32 ) e.preventDefault();
				} );
			}
		} );
		
		return this;
	};
	
	$.fn.extend({
		
		/*
		* input masking plugin
		*
		* function to toggle sensitive information such as passwords and pins
		* changes field from a password field with masked input to a plain text field with readable input
		* expects a collection of inputs with type="password"
		* 
		* ISSUE: setting autocomplete="off" on the input tag, otherwise firefox will generate errors
		*
		* @param scriptOptions: Object, defines options that will override the defaults
		*
		*/
		inputMaskToggle: function(scriptOptions){
			
			var defaults = {
				revealLink: false,				//boolean: adds a reveal link under the input to toggle the field
				revealText: "Reveal Password",	//string: text used for the reveal link
				hideText: "Hide Password",	    //string: text used for the hide link
				toggleOnFocus: true				//boolean: toogles field on focus
			};
			
			var options = $.extend(defaults, scriptOptions);
			
			return this.each(function() {
				var $this = $(this);
				
				//make sure autocomplete is set to off
				$this.attr("autocomplete","off");
				
				//create duplicate input field to hold readable text;
				var newField = $("<input />");
				newField.attr("id", $this.attr("id") + "Text");
				newField.attr("name", $this.attr("name") + "Text");
				newField.attr("class", $this.attr("class"));
				newField.attr("style", $this.attr("style"));
				
				//Turn off autocomplete for duplicate fields
				newField.attr("autocomplete", "off");
				
				//only add maxlength if it's been set
				if($this.attr("maxlength") > 0){
					newField.attr("maxlength", $this.attr("maxlength"));
				}
				newField.attr("type", "text");
				newField.attr("value", $this.val());
				
				newField.insertBefore($this);
				newField.hide();
				if($.browser.msie){
					$this.one("blur",function(e){
						e.stopImmediatePropagation();
						return false;
					});
				}
				
				if(options.toggleOnFocus) {
					$this.bind("focus",function(){
						var $passwordField = $(this);
						var $textField = $passwordField.prev();
						$passwordField.hide();
						$textField.show();
						$textField.focus();
					});
					
					$this.bind("change",function(){
						var $passwordField = $(this);
						var $textField = $passwordField.prev();
						$textField.val($passwordField.val());
					});
				}
				
				newField.bind("blur",function(){
					var $textField = $(this);
					var $passwordField = $textField.next();
					$passwordField.val($textField.val());
					if(options.toggleOnFocus) {
						$textField.hide();
						$passwordField.show();
					}
					$passwordField.triggerHandler("blur");
					$textField.attr("class", $passwordField.attr("class"));
				});
				
				newField.bind("focus",function(){
					//add classes again in case validation produced an error
					var $textField = $(this);
					var $passwordField = $textField.next();
					$textField.attr("class", $passwordField.attr("class"));
				});
				
				if (options.revealLink) {
					
					//var link = $("<div class=\"revealMaskedField\"><a href=\"#" + this.id + "\">" + options.revealText + "</a></div>");
					var link = $(this).parent().find('.revealMaskedField a');
					
					if (link.length == 0)
					{
						link = $("<a href=\"#" + this.id + "\">" + options.revealText + "</a>");
						var linkWrapper = $("<div class=\"revealMaskedField\"></div>");
					
						linkWrapper.html(link);
						$(this).after(linkWrapper);
					}
					
					link.bind("click", function(event) {
						event.preventDefault();
						
						var linkTarget = $(this).attr("href");
						
						linkTarget = linkTarget.substr(linkTarget.indexOf("#"));
						
						var $passwordField = $(linkTarget);
						var $textField = $passwordField.prev();
						
						if($textField.is(":hidden")) {
							$textField.show();
							$passwordField.hide();
							$textField.val($passwordField.val());
							$textField.focus();
							$(this).html(options.hideText);
						}
						else {
							$textField.hide();
							$passwordField.show();
							$passwordField.val($textField.val());
							$passwordField.focus();
							$(this).html(options.revealText);
						}
					});
				}
			});
		}
	});
})(jQuery);

