var pageOptions = {};
var accordionOptions = {};
if (typeof window.HOST === 'undefined') { var HOST = window.location.hostname; }
if (typeof window.PATH === 'undefined') { var PATH = location.protocol + '//' + HOST; }
if (typeof window.CDN_PATH === 'undefined') { var CDN_PATH = location.protocol + '//cdn.boatus.com/'; }
if (typeof window.API_PATH === 'undefined') { var API_PATH = PATH + '/API/2.0/'; }
if (typeof window.PUBLIC_API_KEY === 'undefined') { var PUBLIC_API_KEY = 'ABCDEF12345'; }
if (typeof window.FB_APP_ID === 'undefined') { var FB_APP_ID = ''; }

// Create the Global BoatUS object.
var BoatUS = {
	version: '0.1',
	optsDefaults: {
		debug: false,		// Set to false in production environments.
		ads: {
			disableAds: false,
			personalizeAds: true
		},
		Overlay: {
			fixedWidth: true,
			destroyOnUnload: false
		},
		social: false,
		socialOpts: {
			title: $('title:first').html() || "BoatUS",
			description: $("meta[name~='description']").attr("content") || "",
			url: document.location.href,
			FB: true,
			TW: true,
			DG: true,
			SU: true,
			EM: true,
			caller: '',
			extras: {}
		},
		UI: {
			tabs: {
				save: true,					// Should the most recent tab be saved?
				selected: 1,				// 1-based index of the tab to select by default.
				onChange: null,				// Callback function for the change event.
				onCreate: null				// Callback for after the tabs have been created.
			},
			text: {
				startSize: 0,
				curSize: 0,
				defTarget: '#content',
				minSize: -2,
				maxSize: 5
			}
		}
	},
	Options: {},
	pageReady: false,
	Queue: {
		data: [],
		addToQueue: function(fn)
		{
			// Add the ability to queue up events before the page has loaded.
			if (typeof fn !== "function") { return; }
			this.data.push(fn);
			BoatUS.log("Method called before page ready: ");
			BoatUS.log(fn);
		},
		fireQueue: function()
		{
			// Loop through all the events in the queue once an event has passed.
			var i;
			for (i = 0, l = this.data.length; i < l; i++)
			{
				this.data[i]();
			}
		}
	},
	Events: {},
	EventLog: [],		// An array to hold the index of all the event objects.
	// Initialize everything.
	init: function(opts) {
		BoatUS.Options = $.extend({}, BoatUS.optsDefaults, opts);
		BoatUS.log(BoatUS.Options);
		if (typeof(JSON) !== 'object') { BoatUS.require_once('/assets/js/json.js'); }
		this.pageReady = true;
		this.Queue.fireQueue();
		var initAds = function() { BoatUS.Ads.init(); };
		this.adLoadTimeout = setTimeout(initAds, 3000);
		if (BoatUS.Options.social === true) { this.Social.loadFBAPI(); }
		// Bootstrap the page.
		this.Member.init();
		this.Browser.init();
		this.Overlay.init();
	},
	// Adds the ability to create a popup window.
	Popup: function(strURL, strName, objOpts) {
		objOpts = objOpts || {};
		strOpts = [];
		if (objOpts.status) { strOpts.push('status=1'); } else { strOpts.push('status=0'); }
		if (objOpts.toolbar) { strOpts.push('toolbar=1'); } else { strOpts.push('toolbar=0'); }
		if (objOpts.location) { strOpts.push('location=1'); } else { strOpts.push('location=0'); }
		if (objOpts.resizeable) { strOpts.push('resizeable=1'); } else { strOpts.push('resizeable=0'); }
		if (objOpts.scrollbars) { strOpts.push('scrollbars=1'); } else { strOpts.push('scrollbars=0'); }
		if (objOpts.height !== '') { strOpts.push('height=' + parseInt(objOpts.height, 10)); } else { strOpts.push('height=250'); }
		if (objOpts.width !== '') { strOpts.push('width=' + parseInt(objOpts.width, 10)); } else { strOpts.push('width=350'); }
		return window.open(strURL, strName, strOpts.join(','));
	},
	
	// Adds the ability to create dynamically loading overlays.
	Overlay: {
		init: function()
		{
			this.createDOM();
		},
		initialized: false,
		overlay: function(ovID, ovPath, ovOpts)
		{
			this.ID = ovID;
			this.div = null;
			this.path = ovPath;
			this.opts = $.extend({}, BoatUS.Options.Overlay, ovOpts);
			
			this.onLoad = function(e)
			{
				BoatUS.Overlay.curOverlay = this;
				if (typeof window[this.ID + '_load'] == 'function') window[this.ID + '_load'](e);
			};
			
			this.onOpen = function(e)
			{
				BoatUS.Overlay.curOverlay = this;
				this.position();
				if (typeof window[this.ID + '_open'] == 'function') window[this.ID + '_open'](e);
			};
			
			this.onClose = function(e)
			{
				if (typeof window[this.ID + '_close'] == 'function') window[this.ID + '_close'](e);
				if (this.opts.destroyOnUnload === true) this.destroy();
			};
			
			this.onDestroy = function()
			{
				if (typeof window[this.ID + '_destroy'] == 'function') window[this.ID + '_destroy'](e);
				try
				{
					if (typeof window[this.ID + '_load'] == 'function') window[this.ID + '_load'] = null;
					if (typeof window[this.ID + '_open'] == 'function') window[this.ID + '_open'] = null;
					if (typeof window[this.ID + '_close'] == 'function') window[this.ID + '_close'] = null;
					if (typeof window[this.ID + '_destroy'] == 'function') window[this.ID + '_destroy'] = null;
				}
				catch (ex)
				{
					
				}
			};
			
			this.position = function()
			{
				$(BoatUS.Overlay.overlayDiv).center(window);
			};
			
			this.load = function()
			{
				BoatUS.log('Load Overlay');
				if (BoatUS.Overlay.overlays[this.ID] == null) {
					BoatUS.Overlay.registerOverlay(id, PATH + '/assets/overlay/' + this.ID + '.asp');
				} else {
					this.opts = $.extend({}, this.opts, BoatUS.Overlay.overlays[this.ID].opts);
				}
				var overlayLoadPath = this.path;
				BoatUS.log('Overlay Path: ' + overlayLoadPath);
				var me = this;
				var onHTMLLoaded = function(c, t, s, x, m_opts)
				{
					if(x.status !== 200)
					{
						c = $('<div style="top: 312px; left: 356px;" class="content_container" id="' + this.ID + '"><div onclick="BoatUS.closeOverlay(\'' + id + '\');return false;" class="overlay_close" title="Click to Close"></div><div class="overlay_bdy"><h3>Error</h3></div><p>There was a problem loading the desired content. Please try again later.</p><div class="clear"></div></div>');
					}
					$('.overlay_bdy', BoatUS.Overlay.overlayDiv).empty().append(c.show());
					$(BoatUS.Overlay.overlayDiv).show();
					me.position();
					me.onOpen({ opts: m_opts });
				};
				
				var o_html = '';
				if (!this.opts.params) this.opts.params = {};
				this.opts.params._r = BoatUS.randomString(5);		// Prevent caching.
				
				o_html += '<div style="top: 312px; left: 356px;" class="content_container" id="' + this.ID + '"><div onclick="BoatUS.Overlay.closeOverlay();return false;" class="overlay_close" title="Click to Close"></div><div class="overlay_bdy"><h3>Loading...</h3></div><div class="clear"></div></div>';
				$(BoatUS.Overlay.overlayDiv).html(o_html);
				$(BoatUS.Overlay.overlayDiv).show();
				if ($('#boatus_overlayContent_' + this.ID).length === 0)
				{
					BoatUS.log('Load It!');
					this.div = $('<div />', { id: 'boatus_overlayContent_' + this.ID }).appendTo(BoatUS.Overlay.overlayContainerDiv).hide();
					this.div.load(overlayLoadPath, this.opts.params, function(t, s, x) {
						// If the call failed, remove and destroy the new overlay content container so we can try again.
						if (x.status !== 200)
						{
							me.div.detach();
						} else {
							// On the first open, fire the Load event.
							me.onLoad({ opts: me.opts });
						}
						onHTMLLoaded(me.div, t, s, x, me.opts);
					});
				} else {
					BoatUS.log('Already Exists');
					this.div = $('#boatus_overlayContent_' + this.ID);
					onHTMLLoaded(this.div, null, null, { status: 200 });
					// On subsequent opens, fire the Open event.
					this.onOpen({opts: this.opts });
				}
			};
			
			this.show = function(opts)
			{
				BoatUS.log(opts);
				if (opts) this.opts = opts;
				BoatUS.Overlay.dimmerDiv.show();
				if (this.opts.fixedWidth !== false) { BoatUS.Overlay.overlayDiv.css('width', '600px'); }
				this.load();
			};
			
			this.hide = function()
			{
				BoatUS.Overlay.dimmerDiv.hide();
				BoatUS.Overlay.overlayDiv.find('.overlay_bdy').children('div').detach().appendTo(BoatUS.Overlay.overlayContainerDiv);
				BoatUS.Overlay.overlayDiv.hide();
				this.onClose();
				if (this.opts.destroyOnUnload) this.destroy();
			};
			
			this.destroy = function()
			{
				BoatUS.Overlay.dimmerDiv.hide();
				this.div.remove();
				BoatUS.Overlay.overlayDiv.hide();
				this.onDestroy();
			};
		},
		curOverlay: null,		// A reference to the currently visible overlay.
		overlays: {},			// A hash map storing all of the overlays.
		dimmerDiv: null,			// This holds the dimmer.
		overlayDiv: null,			// This holds the currently visible overlay.
		overlayContainerDiv: null,	// This holds all the loaded overlays.
		registerOverlay: function(ovID, ovPath, opts)
		{
			if (BoatUS.Overlay.overlays[ovID]) return;
			BoatUS.Overlay.overlays[ovID] = new BoatUS.Overlay.overlay(ovID, ovPath, opts);
		},
		destroyOverlay: function(ovID)
		{
			
		},
		createDOM: function()
		{
			if (this.initialized) return false;
			this.overlayContainerDiv = $('<div />', { 'id': 'boatus_overlay_container', css: { 'display': 'none' }}).appendTo('body');
			this.dimmerDiv = $('<div />', { 'id': 'boatus_overlayDimmer', css: { 'background-color': '#000', 'position': 'fixed', 'z-index': 102003403, 'top': 0, 'left': 0, 'right': 0, 'bottom': 0, 'opacity': 0.5, 'display': 'none' }}).appendTo('body').click(function() { BoatUS.Overlay.closeOverlay(); });
			this.overlayDiv = $('<div />', { 'id': 'boatus_overlay', css: { 'background-color': '#FFF', 'z-index': 102003408, 'display': 'none' }}).appendTo('body');
			this.initialized = true;
		},
		closeOverlay: function()
		{
			this.curOverlay.hide();
			this.curOverlay = null;
		}
	},
	
	handleOverlay: function(strOVName, objOpts)
	{
		if (typeof BoatUS.Overlay.overlays[strOVName] == 'undefined') {
			BoatUS.Overlay.registerOverlay(strOVName, '/assets/overlay/' + strOVName + '.asp');
		}
		BoatUS.Overlay.overlays[strOVName].show(objOpts);
	},
	
	closeOverlay: function()
	{
		BoatUS.Overlay.closeOverlay();
	},
	
	// The Member object allows us to store and access information pertaining to the member if they are logged in to my.boatus.com.
	Member: {
		loggedIn: false,
		info: {
			memberNumber: 0,
			lastVisit: new Date(),
			realName: {
				first: '',
				last: ''
			},
			emailAddress: '',
			zipCode: '',
			hasTowing: false,
			hasInsurance: false
		},
		optInAdPersonalization: false,	// Allows us to turn off personalized ads.
		init: function()
		{
			// Load up information about the current user from the API. Once that information is loaded, we can use that information on the page.
			// In production, this information would be obtained from the services API, and then returned as a JSON object.
			//this.onDataLoaded({ loggedIn: true, realName: { first: 'Andrew' }, boatType: 'power'})
		},
		getAdMember: function()
		{
			// Return an object containing certain data that we can use to personalize ads.
			if (! this.optInAdPersonalization) { return { memberNumber: 0, towing: false, insurance: false }; }
			return { memberNumber: this.info.memberNumber, towing: this.info.hasTowing, insurance: this.info.hasInsurance };
		},
		onDataLoaded: function(r)
		{
			// Data has returned from the API.
			clearTimeout(BoatUS.adLoadTimeout);
			BoatUS.Ads.init();
			var v;
			for (v in r) {
				if (typeof this.info[v] !== 'function') { this.info[v] = r[v]; }	// Copy all of the response variables in, but don't overwrite functions.
			}
			// This code could turn the log in to my.boatus.com link into a goto boatus.com link if they are logged in.
			if (this.loggedIn) {
				$('.myBoatUSLogin').attr('onclick', '').attr('href', 'http://my.boatus.com/memberpage.asp').html('Go To my.BoatUS.com');
			}
			BoatUS.triggerEvent('Member.Load', { member: this.info.memberNumber });
		}
	},
	
	// The BoatUS Ads object allows us to embed advertising in the page in a non-blocking way.
	Ads :{
		adZones: [],
		initialized: false,
		strAdZoneSelector: 'busa',
		init: function()
		{
			if (this.initialized === true || BoatUS.Options.ads.disableAds === true) { return; }	// Make sure that we only initialize the advertising object once.
			this.initialized = true;



			$('.' + this.strAdZoneSelector).each(function(i){
				BoatUS.Ads.adZones.push($(this).attr('id'));
			});
			// IE gives errors if the API is on a different domain, so disable it.
			//$.post(API_PATH, { zones: BoatUS.Ads.adZones, member: BoatUS.Member.getAdMember() }, function(d,s,x) { BoatUS.Ads.onDataLoaded(d, s, x); });
		},
		onDataLoaded: function(d, s, x)
		{
			// The ad information has been retrieved from the server, lets do something with it.
			BoatUS.triggerEvent('Ads.Load', { });
		},
		renderImageAd: function(ad)
		{
			// Generate an image ad on the page.
		},
		renderFlashAd: function(ad)
		{
			// Generate a swf ad on the page.
		},
		renderTextAd: function(ad)
		{
			// Generate a text ad on the page.
		}
	},
	
	SWF: {
		expressInstallSwfUrl: '/assets/swf/expressInstall.swf',
		videos: {},
		styles: {
			playButton_Default: 'videoPreviewButton',
			playButton_Hover: ''
		},
		videoPlayerSwfUrl: 'http://www.boatus.com/membership/latitudes.swf',
		flashPlayerVersion: '9.0.0',
		flashVarDefs: {},
		flashParamDefs: {
			menu: 'false',
			quality: 'high',
			scale: 'noscale',
			loop: 'false',
			wmode: 'transparent',
			allowScriptAccess: 'sameDomain'
		},
		flashAttrDefaults: {},
		embed: function(swfUrl, id, width, height, flashvars, params, attributes, callbackFn)
		{
			params = $.extend({}, this.flashParamDefs, params);
			flashvars = $.extend({}, this.flashVarDefs, flashvars);
			if (!BoatUS.pageReady) {
				BoatUS.Queue.addToQueue(function() {
					BoatUS.SWF.embed(swfUrl, id, width, height, flashvars, params, attributes, callbackFn);
				});
				return;
			}
			var targetDiv = $('#' + id);
			BoatUS.require_once('/assets/js/swfobject/swfobject.js');
			swfobject.embedSWF(swfUrl, id, width, height, BoatUS.SWF.flashPlayerVersion, this.expressInstallSwfUrl, flashvars, params, attributes, callbackFn);
			BoatUS.triggerEvent("SWF.Load", { swf: swfUrl });
		},
		// Embed a video with a preview frame in it.
		embedVideo: function(id, width, height, videoSrc, firstFrameSrc, flashVars, params, attributes)
		{
			params = $.extend({}, this.flashParamDefs, params);
			if (!BoatUS.pageReady) {
				BoatUS.Queue.addToQueue(function() {
					BoatUS.SWF.embedVideo(id, width, height, videoSrc, firstFrameSrc, flashVars, params, attributes);
				});
				return;
			}
			BoatUS.require_once('/assets/js/swfobject/swfobject.js');
			var videoID = BoatUS.uniqueID();
			this.videos[videoID] = { src: videoSrc, firstFrame: firstFrameSrc, id: id, width: width, height: height, flashVars: flashVars, params: params, attributes: attributes };
			var targetDiv = $('#' + id).css('position', 'relative');
			targetDiv.empty();
			//	create a holder div to load the preview into.
			var embedDiv = $('<div/>', { css: { width: width, height: height, margin: '0 0 8px 0' } }).addClass('videoEmbed').appendTo(targetDiv);
			var thumbLink = $('<a/>', { css: { position: 'absolute', height: height, width: width }, title: 'Click to Play Video', href: '#', click: function() { BoatUS.SWF.playVideo(videoID); return false; } }).addClass('noHover').addClass('noLine').appendTo(embedDiv);
			thumbLink.append($('<img/>', { css: { margin: 0 }, src: firstFrameSrc, width: parseFloat(width), height: parseFloat(height)}));
			var playBtn = $('<span/>', { css: { height: '75px', width: '75px' } }).addClass('playBtn').appendTo(thumbLink);
		},
		playVideo: function(id)
		{
			var curVideo = this.videos[id];
			this.embed(this.videoPlayerSwfUrl, curVideo.id, curVideo.width, curVideo.height, curVideo.flashVars, curVideo.params, curVideo.attributes);
		}
	},
	
	Validate: {
		email: function(strEmail)
		{
			var reg = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;
			return reg.test(strEmail);
		}
	},
	
	Social: {
		loadFBAPI: function()
		{
			window.fbAsyncInit = function() {
				FB.init({appId: FB_APP_ID, status: true, cookie: true, xfbml: true});
			};
			if ($('#fb-root').length === 0) { $('body').prepend($('<div id="fb-root"></div>')); }		// The Facebook API needs a div to attach it's scripts to.
			var e = document.createElement('script'); e.async = true;
			e.src = document.location.protocol + '//connect.facebook.net/en_US/all.js';
			document.getElementById('fb-root').appendChild(e);
		},
		share: function(args)
		{
			args = BoatUS.Social.parseOpts(args);
			BoatUS.handleOverlay('share', args);
		},
		parseOpts: function(args)
		{
			// go through the options the the args hash and return the set to use.
			var shareDefs = BoatUS.Options.socialOpts;
			args = $.extend({}, shareDefs, args);
			args.title = unescape(args.title);
			return args;
		},
		shareFB: function(args)
		{
			args = BoatUS.Social.parseOpts(args);
			args.url = BoatUS.Utils.addQueryParameter(args.url, 'WT.src', 'share_FB');
			strShareLink = 'http://www.facebook.com/sharer.php?u=' + encodeURIComponent(args.url) + '&t=' + encodeURIComponent(args.title);
			BoatUS.Popup(strShareLink, 'facebookShare', { width: 650, height: 300 });
			BoatUS.Social.wtTrackShare('Facebook', args.title, args.url);
			return false;
		},
		shareTW: function(args)
		{
			args = BoatUS.Social.parseOpts(args);
			args.url = BoatUS.Utils.addQueryParameter(args.url, 'WT.src', 'share_TW');
			strShareLink = 'http://twitter.com/share?url=' + encodeURIComponent(args.url) + '&text=' + encodeURIComponent(args.title) + '&via=BoatUS';
			BoatUS.Popup(strShareLink, 'twitterShare', { width: 650, height: 200 });
			BoatUS.Social.wtTrackShare('Twitter', args.title, args.url);
			return false;
		},
		shareDG: function(args)
		{
			args = BoatUS.Social.parseOpts(args);
			args.url = BoatUS.Utils.addQueryParameter(args.url, 'WT.src', 'share_DG');
			strShareLink = 'http://digg.com/submit?url=' + encodeURIComponent(args.url) + '&title=' + encodeURIComponent(args.title) + '&bodytext=' + encodeURIComponent(args.description);
			BoatUS.Popup(strShareLink, 'diggShare', { width: 1050, height: 655 });
			BoatUS.Social.wtTrackShare('Digg', args.title, args.url);
			return false;
		},
		shareSU: function(args)
		{
			args = BoatUS.Social.parseOpts(args);
			args.url = BoatUS.Utils.addQueryParameter(args.url, 'WT.src', 'share_SU');
			strShareLink = 'http://www.stumbleupon.com/submit?url=' + encodeURIComponent(args.url) + '&title=' + encodeURIComponent(args.title);
			BoatUS.Popup(strShareLink, 'stumbleShare', { width: 720, height: 500});
			BoatUS.Social.wtTrackShare('Stumble Upon', args.title, args.url);
			return false;
		},
		shareEM: function(args)
		{
			args = BoatUS.Social.parseOpts(args);
			args.url = BoatUS.Utils.addQueryParameter(args.url, 'WT.src', 'share_EM');
			BoatUS.log(args);
			BoatUS.handleOverlay('sendToFriend', args);
		},
		wtTrackShare: function(strService, strTitle, strURL)
		{
			try {
				dcsMultiTrack('WT.ria_a', 'Social', 'WT.ria_ev', 'Share', 'DCSext.smService', strService, 'WT.ria_c', strTitle + ' (' + strURL + ')', 'DCSext.url', strURL);
			} catch (e) {
		
			}
		},
		Comments: {
			comment_template: '<li id="cmt-{{identifier}}" class="cmt-row nest-level-{{nest}}"><h3> {{authorname}} </h3><p>{{text}}</p></li>',
			getComments: function (threadID, settings)
			{
				var defs = {
					comment_count: 100,
					comment_order: 'newest',
					thread_identifier: threadID,
					thread_url: document.location.href,
					thread_title: BoatUS.Options.socialOpts.title,
					template: BoatUS.Social.Comments.comment_template,
					comment_target: '#comment-thread'
				};
				var settings = $.extend({}, settings, defs);
				if ($(settings.comment_target).length < 1) { return; }		// If the placeholder for the comments doesn't exist, don't bother loading them.
				BoatUS.Social.Comments.template = new BoatUS.UI.Template(settings.template);
				$.ajax({
					url: API_PATH + 'comments/',
					data: settings,
					cache: true,
					error: function(h, s, e) {
						// Something went wrong.
						BoatUS.log(e);
					},
					success: function(d, t, x) {
						// Everything went OK and data was returned from the server.
						var cmts_html = '';
						if (d.comments.length > 0) {
							for (var ci = 0; ci < d.comments.length; ci++) {
								cmts_html += BoatUS.Social.Comments.printComment(d.comments[ci], 0);
							}
							$(d.target).html(cmts_html);
						}
					},
					type: 'POST'
				});
			},
			printComment: function(oComment, iNest)
			{
				var cmt_data = {
					identifier: oComment.id,
					authorname: oComment.author.username,
					text: oComment.text,
					nest: iNest
				};
				BoatUS.log(cmt_data);
				var cmt_html = BoatUS.Social.Comments.template.render(cmt_data);
				var cmt_replies = '';
				if (oComment.replies.length > 0) {
					for (var rpi = 0; rpi < oComment.replies.length; rpi++) {
						cmt_replies += BoatUS.Social.Comments.printComment(oComment.replies[rpi], iNest+1);
					}
				}
				return cmt_html + cmt_replies;
			},
			leaveComment: function()
			{
				
			},
			printCommentForm: function(strTarget)
			{
				
			}
		}
	},
	// The Geo API contains all of the methods to allow us to load and manipulate geographic data.
	Geo: {
		appState: {
			NOT_LOADED: -1,
			INITIALIZING: 0,
			READY: 1,
			LOADING_DATA: 2,
			DATA_LOADED: 3,
			RENDERING: 4
		},
		mapSize: {
			SMALL: 0,
			MEDIUM: 1,
			LARGE: 2,
			XLARGE: 3
		}
	},
	// The UI API allows us to build rich UI interactions.
	UI: {
		Template: function(str_tmpl)
		{
			this.template_src = str_tmpl;
			this.render = function(d) {
				var rT = this.template_src;
				var re = new RegExp ("\{\{(.*?)\}\}","gm");		// @TODO: Fix RegEx pattern to not freak out about > brackets in front of the matching pattern.
				var m;
				while (m = re.exec(rT)) {
					rT = rT.replace(m[0], d[m[1]]);
				}
				return rT;
			}
		},
		// Create a tabbed interface.
		tabs: {
			init: function(strContainerID, objOpts)
			{
				var oOpts = {};
				if (!BoatUS.pageReady) {
					// The UI call was too early. Defer until the page has fully loaded.
					BoatUS.Queue.addToQueue(function() {
						BoatUS.UI.tabs.init(strContainerID, objOpts);
					});
					return;
				}
				oOpts = $.extend({}, BoatUS.Options.UI.tabs, objOpts);
				if ($(strContainerID).length > 1) {
					// The tabs function must only be called on a single element. If more than one element is passed, loop through them.
					$(strContainerID).each(function(i, e) {
						BoatUS.UI.tabs.init(this, oOpts);
					});
					return;
				}
				var container, tabsID, curTab;
				if ($(strContainerID).length !== 1) { return; }
				container = $(strContainerID);
				$(container).addClass('tabbed-module');
				tabsID = container.attr('id');
				if (oOpts.save) { curTab = BoatUS.Data.get('tabs_' + tabsID); } else { BoatUS.Data.remove('tabs_' + tabsID); }
				if (curTab !== '') {
					if ($('#' + curTab).length < 1) { curTab = ''; }
				}
				//if (oOpts.selected - 1 >= container.children('div').length) { oOpts.selected = 1; }
				if (curTab === '' || curTab === null || curTab === undefined) { curTab = container.children('div').eq(oOpts.selected - 1); } else { curTab = '#' + curTab; }
				container.children('div').not(curTab).hide();
				$('ul.tabs > li', container).each(function(i, e) {
					$('a', this).click(function(e) {
						BoatUS.UI.tabs.changeTab(this);
						return false;
					});
				});
				curTab = $(curTab, container);
				curTab.addClass('current').show();
				$('a[rel="' + curTab.attr('id') + '"]').parent().addClass('current');
				if (typeof oOpts.onChange === "function") {
					$(container).data('onChange', oOpts.onChange);
					oOpts.onChange({ target: curTab, type: 'click', tabID: $(curTab).attr('rel') }, container);
				}
				if (typeof oOpts.onCreate === "function") {
					oOpts.onCreate(container, oOpts);
				}
			},
			// Change to the given tab.
			changeTab: function(arg0, arg1)
			{
				var objTab, clickedTab, clickTabGroup, targetTab;
				if (arg1 === undefined) {
					// if only 1 argument was passed, that argument must be the jquery pointer to the tab to show.
					objTab = arg0;
				} else {
					// otherwise arg0 is the container, and arg1 is the tab index.
					objTab = $('li', arg0).eq(arg1).find('a');
				}
				clickedTab = $(objTab).parent();
				clickedTabGroup = $(objTab).closest('.tabbed-module');
				targetTab = $('#' + $(objTab).attr('rel'));
				clickedTabGroup.find('li').removeClass('current');
				clickedTab.addClass('current');
				BoatUS.Data.set('tabs_' + clickedTabGroup.attr('id'), $(objTab).attr('rel'));
				clickedTabGroup.children('div').hide();
				$(targetTab).show();
				if (typeof clickedTabGroup.data('onChange') === "function") {
					clickedTabGroup.data('onChange')({ target: objTab, type: 'click', tabID: $(objTab).attr('rel') }, clickedTabGroup);
				}
			}
		},
		// work with text
		text: {
			initialized: false,
			init: function()
			{
				// maybe have some bootstrap code when it's first called.
			},
			// Make the text larger or smaller.
			resize: function(strTarget, amt, nest)
			{
				BoatUS.log(amt);
				if (!this.initialized) { this.init(); }
				if ($(strTarget).hasClass('noResize')) { return false; }
				if (amt === 0) { $(strTarget).css('font-size', ''); }
				if (amt < 0) {
					if (BoatUS.Options.UI.text.curSize - Math.abs(amt) < BoatUS.Options.UI.text.minSize) { return false; }
				} else {
					if (BoatUS.Options.UI.text.curSize + Math.abs(amt) > BoatUS.Options.UI.text.maxSize) { return false; }
				}
				if (amt === 0) { BoatUS.Options.UI.text.curSize = 0; }
				if (nest !== true) {
					BoatUS.Options.UI.text.curSize += amt;
				}
				$('*', strTarget).contents().css('font-size',((1 + (BoatUS.Options.UI.text.curSize * .1)) * 100) + '%');
				return false;
			}
		},
		paginator: function(target, opts) {
			var defs = {
				showFirstLast: false,		// Should we show first / last links?
				pageVariable: "page",		// What variable should hold the page number?
				baseQS: "",				// The base QueryString that will be the same for all links.
				dividerText: "&hellip;",		// The text to be rendered in between the buttons.
				showMax: 4,				// How many links should be shown at once?
				pages: 0,
				curPage: 0,
				style: {					// Style properties.
					buttonClass: "paginatorButton",
					currentClass: "selected",
					nextClass: "next",
					prevClass: "prev",
					firstClass: "first",
					lastClass: "last",
					dividerClass: "break",
					holderClass: "paginator"
				},
				onChange: null				// Function that gets passed the page changed event.
			};
			this.options = $.extend({}, defs, opts);
			BoatUS.log(this.options);
			this.target = $(target);
			this.pages = this.options.pages;
			this.curPage = this.options.curPage;
			var links = [];
			var firstLink = null, lastLink = null;
			var holder = null;
			// Create the necessary html to display the pagination.
			this.init = function()
			{
				links = [];
				firstLink = null;
				lastLink = null;
				holder = null;
			};
			this.generate = function()
			{
				this.init();
				holder = $('<div />', { }).addClass(this.options.style.holderClass);
				var me = this;
				BoatUS.log("PAGE COUNT: " + this.pages);
				if (this.pages <= 1) return;
				var cS = 1;
				//if (this.curPage > 4) cS = this.curPage - 4;
				//if (cS > 1) {
					//if (this.options.showFirstLast) {
						firstLink = $('<a />', { html: 'First' }).attr({ 'href': this.getPageLink(0), title: 'Go to First Page' }).addClass(this.options.style.firstClass).click(function() { return me.changePage(0); }).hide();
						if (this.options.showFirstLast) firstLink.appendTo(holder).css('display', 'inline');
					//}
					//$('<span />', { html: this.options.dividerText }).addClass(this.options.style.dividerClass).appendTo(holder);
				//}
				for (var i = 0; i < this.pages; i++) {
					//if (cS <= this.pages) {
						//if (cS - 1 != this.curPage) {
							links.push($('<a />', { html: i + 1 }).attr({ 'href': '#', title: 'Page ' + i }).addClass(this.options.style.buttonClass).click(function() { return me.changePage($(this).data('msl-page')); }).data('msl-page', i));
							links[i].appendTo(holder).hide();
						//} else {
						//	$('<span />', { html: cS }).addClass(this.options.style.currentClass).appendTo(holder);
						//}
					//}
				}
				//if (cS <= this.pages) {
					//$('<span />', { html: this.options.dividerText }).addClass(this.options.style.dividerClass).appendTo(holder);
					//if (this.options.showFirstLast)
					lastLink = $('<a />', { html: 'Last' }).attr({ 'href': this.getPageLink(this.pages - 1), title: 'Go to Last Page' }).addClass(this.options.style.lastClass).click(function() { alert("LAST!"); return me.changePage(me.pages - 1); }).hide();
					if (this.options.showFirstLast) lastLink.appendTo(holder).css('display', 'inline');
					
				//}
				//pHTML += '</div>';
				return holder;
			};
			// Change the page that we are on.
			this.changePage = function(i)
			{
				this.curPage = i;
				var cnt = false;
				if (i < 0 || i > this.pages) return false;
				if (this.options.onChange) {
					cnt = this.options.onChange(i);		// If the onChange callback returns true, allow the link to function as normal.
				}
				if (cnt) return true;
				this.updateUI();
				return false;
			};
			// Change the UI to reflect the new page number.
			this.updateUI = function(returnHTML)
			{
				var cS = 0;
				if (this.curPage > 4) cS = this.curPage - 4;
				
				var showStart = cS;		// Which page link should we start displayings?
				var showStop = cS + 10;		// Which page link should we end displaying?
				BoatUS.log(showStart);
				if (showStart > 0) {
					firstLink.css('display', 'inline');
				} else {
					firstLink.css('display', 'none');
				}
				var cL = null;
				for (var i = 0, j = links.length; i < j; i++) {
					cL = links[i];
					cL.removeClass('selected');
					if (i >= showStart && i <= showStop) {
						cL.show();
						if (i === this.curPage) cL.addClass('selected');
					} else {
						cL.hide();
					}
				}
				if (returnHTML) return holder;
				//return this.generate(true);
			};
			// Return the link to a given page.
			this.getPageLink = function(p)
			{
				if (this.options.baseQS != "") {
					var oQS = BoatUS.Utils.qsToObject(this.options.baseQS);
					oQS['page'] = p;
					var sQS = BoatUS.Utils.objectToQS(oQS);
					return '?' + sQS;
				} else {
					return '?' + this.options.pageVariable + '=' + p;
				}
			};
		}
	},
	// The data API gives us high-level access to any available browser storage APIs.
	Data: {
		get: function(strKey)
		{
			if (typeof(JSON) !== 'object') { return; }		// We can only retrieve data if the JSON methiods are available.
			if (strKey === "" || strKey === undefined || strKey === null) { return ""; }
			var varVal = '';
			if (window.sessionStorage) {
				if (sessionStorage.getItem(strKey) !== "") {
					varVal = JSON.parse(sessionStorage.getItem(strKey));
				}
				BoatUS.log("DATA.GET: " + strKey + " = " + varVal);
			} else {
				// provide some sort of fallback. (Cookies, maybe).
				
			}
			return varVal;
		},
		set: function(strKey, varVal)
		{
			if (typeof(JSON) !== 'object') { return; }		// We can only store data if the JSON methiods are available.
			if (window.sessionStorage) {
				sessionStorage.setItem(strKey, JSON.stringify(varVal));
			} else {
				// provide some sort of fallback. (Cookies, maybe).
				
			}
			BoatUS.log("DATA.SET: " + strKey + " = " + varVal);
		},
		remove: function(strKey)
		{
			if (window.sessionStorage) {
				sessionStorage.removeItem(strKey);
			} else {
				// provide some sort of fallback. (Cookies, maybe).
				
			}
			BoatUS.log("DATA_REMOVE: " + strKey);
		}
	},
	Utils: {
		// Given a QueryString-formatted string, return an object.
		qsToObject: function(qs)
		{
			var retO = {}, qsO = qs.split("&"), val = "";
			for (var i = 0, j = qsO.length; i < j; i++)
			{
				val = qsO[i].split("=");
				if (retO[val[0]]) {
					// There is already a key with this name, so we need to store an array of the info.
					if (!typeof retO[val[0]] == "array") { 
						var bakV = retO[val[0]];
						retO[val[0]] = [];
						retO[val[0]].push(bakV);
					}
					retO[val[0]].push(val[1]);
				} else {
					retO[val[0]] = val[1];
				}
			}
			return retO;
		},
		// Given a JavaScript object, return a QueryString-formatted string.
		objectToQS: function(obj)
		{
			var qsRet = [];
			for (var p in obj) {
				if (obj[p] == undefined) continue;
				qsRet.push(p + '=' + obj[p]);
			}
			return qsRet.join("&");
		},
		addQueryParameter: function(strURL, param, value)
		{
			var strQS = BoatUS.Utils.qsOnly(strURL);
			var strURL = BoatUS.Utils.urlOnly(strURL);
			var qs = BoatUS.Utils.qsToObject(strQS);
			if (typeof param == "object") {
				for (var k in param) {
					if (k == undefined || param[k] == undefined) continue;
					qs[k] = encodeURIComponent(param[k]);
				}
			} else {
				qs[param] = value;
			}
			var ret = strURL + '?' + BoatUS.Utils.objectToQS(qs);
			return ret;
		},
		qsOnly: function(strURL)
		{
			strURL = unescape(strURL);
			if (strURL.indexOf('?') > 0) {
				return strURL.split('?')[1];
			} else {
				return '';
			}
		},
		urlOnly: function(strURL)
		{
			strURL = unescape(strURL);
			if (strURL.indexOf('?') > 0) {
				return strURL.split('?')[0];
			} else {
				return strURL;
			}
		}
	},
	// Store information about the current user agent. Useful for page debugging.
	Browser: {
		properties: {
			type: "",
			version: "",
			cookies: "",
			platform: "",
			agent: "",
			flash: ""
		},
		init: function(opts)
		{
			this.properties.type = navigator.appName;
			this.properties.version = navigator.appVersion;
			this.properties.cookies = navigator.cookieEnabled;
			this.properties.platform = navigator.platform;
			this.properties.agent = navigator.userAgent;
			this.properties.flash = navigator.mimeTypes && navigator.mimeTypes["application/x-shockwave-flash"];
		}
	},
	// Triggers a previously bound event.
	triggerEvent: function(eventType, eventArgs)
	{
		if (typeof this.Events[eventType] === 'object') {
			var f;
			for (f in this.Events[eventType]) {
				if (typeof this.Events[eventType][f] === 'function') { 
					eventArgs.eventType = eventType;
					this.Events[eventType][f](eventArgs);
				}
			}
		}
	},
	// Adds an event to the BoatUS object. The event may later be triggered with BoatUS.triggerEvent( ... ). Event names should be in the format [Module].[Action] (i.e. Overlay.Open)
	addEventListener: function(eventType, objFunc)
	{
		if (this.Events[eventType] === undefined) { this.Events[eventType] = []; }
		this.Events[eventType].push(objFunc);
		this.EventLog.push([eventType, this.Events[eventType].length]);
		return this.EventLog.length;	// A numeric identifier representing the array index where this event is stored.
	},
	removeEventListener: function(identifier)
	{
		var evt = this.EventLog[identifier - 1];
		this.Events[evt[0]][evt[1]] = null;
	},
	// Include a JavaScript file into the page.
	require: function(url, type, callback)
	{
		if (this.Includes === undefined) { this.Includes = {}; }
		if (type == undefined || type == "script") {
			$.getScript(url, callback);
			//$('<script>', { type: 'text/javascript', src: url }).appendTo($('head').first());
		} else if (type == "stylesheet") {
			$('<link>', { rel: 'stylesheet', href: url }).appendTo($('head').first());
		}
		return;
	},
	// Include a JavaScript file into the page exactly once.
	require_once: function(url, type)
	{
		if (this.Includes === undefined) { this.Includes = {}; }
		if (this.Includes[url]) { return; }
		this.require(url, type);
		this.Includes[url] = true;
		return;
	},
	// Abstracts the logging interface so that we can log from any browser.
	log: function(strMsg)
	{
		if (BoatUS.Options.debug === false || BoatUS.Options.debug === null) { return; }
		if (typeof console !== "undefined") {
			console.log(strMsg);
		}
	},
	warn: function(strMsg)
	{
		if (BoatUS.Options.debug === false || BoatUS.Options.debug === null) { return; }
		if (typeof console !== "undefined") {
			console.warn(strMsg);
		}
	},
	randomString: function(string_length)
	{
		if (string_length === undefined) { string_length = 8; }
		var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz';
		var randomstring = '';
		var i;
		for (i = 0; i < string_length; i++) {
			var rnum = Math.floor(Math.random() * chars.length);
			randomstring += chars.substring(rnum,rnum+1);
		}
		return randomstring;
	},
	uniqueID: function()
	{
		if (typeof BoatUS.incrementID === 'undefined') { BoatUS.incrementID = 0; }
		BoatUS.incrementID++;
		return BoatUS.incrementID;
	}
};
BoatUS.addEventListener('Ads.Load', function(e) { console.log('ads loaded'); });
// Call the Bootstrap function when everything is ready. Events should be bound before this init function is called.
$(document).ready(function() {  BoatUS.init((typeof pageOptions !== 'undefined') ? pageOptions : {}); $('.leftNav').accordionMenu((typeof accordionOptions !== 'undefined') ? accordionOptions : {}); $('.zebra').jqStripe(); });
 
(function($) {
	$.fn.accordionMenu = function(options) {
		var defaults = {
			startCollapsed: true,
			current: '',
			onlyOne: false		// If set to true, only one sub-menu will be open at once.
		};
		var opts = $.extend({}, defaults, options);
		$(opts.current).addClass('current');
		$('#topNav ul.subNav').each(function(i, e) {
			$(this).parent().addClass('hasSub');
		});
		return this.each(function(i) {
			buildMenu(this, opts);
		});
	};
	
	var buildMenu = function(el, opts)
	{
		var parent = $(el);
		var startCollapsed = opts.startCollapsed || false;
		$(parent).children('li').each(function(i, e) {
			if ($('ul.subNav', e).length > 0) {
				startCollapsed = true;
				if ($(e).has('.expanded').length <= 0) { startCollapsed = true; } else { startCollapsed = false; }
				if ($(opts.current, e).length > 0) { startCollapsed = false; }
				if (startCollapsed)
				{
					$(e).addClass('closed').children('ul').not('.expanded').slideUp('fast');
				} else {
					$(e).addClass('open').children('ul').slideDown('fast');
				}
				$(e).children('.expanded').slideDown('fast').parent().removeClass('closed').addClass('open');
				if (!$(e).hasClass('hasSub')) {
					$(e).addClass('hasSub').find('a:first').attr('title', 'Click to Expand Menu').click(function(){
						var justChanged = false;
						if (opts.onlyOne) {
							$(this).parent().parent().parent().find('ul.subNav').not($(this).parent().next('ul.subNav')).slideUp('fast');
							$(this).parent().parent().parent().find('li').removeClass('open').addClass('closed');
							justChanged = true;
						}
						$(this).parent().next('ul.subNav').slideToggle('fast', function(e) {
							if ($(this).css('display') === 'none')
							{
								$(this).parent().removeClass('open').addClass('closed');
							} else {
								$(this).parent().removeClass('closed').addClass('open');
							}
						});
						return false;
					}).wrap('<div />');
				}
				if ($('ul.subNav', e).length > 0) {
					$('ul.subNav', e).accordionMenu(opts);
				}
			}
		});
	};
})(jQuery);

(function($){$.fn.collapseMenu=function(options){var defaults={onlyOne:true,linkSelector:'li > a',toggleSelector:'div.section'};var opts=$.extend({},defaults,options);$(opts.current).addClass('current');return this.each(function(i){buildCollapse(this,opts)})};var buildCollapse=function(el,opts){var parent=$(el);var slides=$(opts.toggleSelector,parent);slides.slideUp();$(opts.linkSelector,parent).each(function(){$(this).click(function(){var tgt=$($(this).attr('rel'));if(tgt.hasClass('expanded')){tgt.slideUp().removeClass('expanded');$(this).removeClass('expanded')}else{if(opts.onlyOne){$(opts.linkSelector,parent).not($(this)).removeClass('expanded');slides.not(tgt).slideUp().removeClass('expanded')}tgt.slideDown().addClass('expanded');$(this).addClass('expanded')}return false})})}})(jQuery);

 
(function($){$.fn.extend({center:function(options){var opts=$.extend({inside:window,transition:0,minX:0,minY:0,vertical:true,withScrolling:true,horizontal:true},options);return this.each(function(){var props={position:'absolute'};if(opts.vertical){var top=($(opts.inside).height()-$(this).outerHeight())/2;if(opts.withScrolling){top+=$(opts.inside).scrollTop()||0}top=(top>opts.minY?top:opts.minY);$.extend(props,{top:top+'px'})}if(opts.horizontal){var left=($(opts.inside).width()-$(this).outerWidth())/2;if(opts.withScrolling){left+=$(opts.inside).scrollLeft()||0}left=(left>opts.minX?left:opts.minX);$.extend(props,{left:left+'px'})}if(opts.transition>0){$(this).animate(props,opts.transition)}else{$(this).css(props)}return $(this)})}})})(jQuery); 

(function($){$.fn.jqStripe=function(params){var _params={stripeClasses:['odd','even'],rowSelector:'tbody > tr:visible'};params=$.extend(_params,params);var stripes=params.stripeClasses.length;var classes=params.stripeClasses.join(' ');return this.each(function(){$(params.rowSelector,this).each(function(i){$(this).not('.noStripe').removeClass(classes).addClass(params.stripeClasses[i%stripes]).hover(function(){$(this).addClass('rowHover')},function(){$(this).removeClass('rowHover')})})})}})(jQuery);


