/**
 * Javascript library for core functionality of Scripps Interactive Newspapers Group websites.
 * @author Ryan Berg <rberg@scrippsweb.com>
 * @author Matt Heisig <mheisig@scrippsweb.com>
 * @namespace jSING
 */
/*
	MODULE: comments designate where each bit of code will fit when the news/entertainment branching occurs
	Options: core|new|ent|mixed
*/
var jSING = {
	/*
	MODULE:core
	 */
	utils: {
		/**
		 * Returns the last bit of a url split by "/"
		 * @method getTargetID
		 * @param {jQuery object} item The item with an href attribute
		 * @return {String} The anchor of the URL: /news/entertainment/#anchorText returns "#anchorText"
		 * Modified by Ryan Berg October 21, 2009
		 */
		getTargetID: function (item) {
			var href = item.attr("href"),
				bits = href.split(/\//),
				id = bits[bits.length-1];
			if (id.charAt(0) == "#") {
				return id; // If the last bit begins with an anchor, return it
			} else {
				return null;
			}
		},
		/*
		Method to retrieve a GET parameter from the URL
		Example:
			For the url "/path/to/page/?a=b&amp;c=d"
			jSING.utils.getAttr('a') will return 'b'
			jSING.utils.getAttr('b') will return 'd'
			jSING.utils.getAttr('x') will return '' since this key does not exist

		Taken from http://www.netlobo.com/url_query_string_javascript.html
		*/
		getUrlAttr: function(key) {
			key = key.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");

			var regexS = "[\\?&]"+key+"=([^&#]*)",
				regex = new RegExp( regexS ),
				results = regex.exec( window.location.href );

			if( results == null ) {
				return "";
			} else {
				return results[1];
			}
		}
	},
	

	/* 
	MODULE: ent 
	*/
	checkBoxSelect: {
		/*
		Used to Check and unCheck checkboxes on forms. Needs a fieldset to work
			For all the advanced searches on Ent
		*/
		checkUncheck: function() {
			$('.checkAll').click(function () {
				$(this).parents('fieldset:eq(0)').find(':checkbox').attr('checked', 'checked');
			});
			$('.unCheckAll').click(function() {
				$(this).parents('fieldset:eq(0)').find(':checkbox').attr('checked', false)
			})
		},
		
		init:function() {
			var self = this;
			self.checkUncheck();
		}
	
		
	},
	
	/*
	MODULE:ent
	*/
	lightBox: {
		/*
		Used for Advanced Search on Ent 
			Pulled from Dialog library in jQuery UI
		*/
		lightBoxEnt: function() {
			$("#advancedSearch").dialog({
				autoOpen: false,
				width: 765,
	//			minHeight: 700,
				modal: true
			});
			$("#advancedSearchClick").click(function() {
				$("#advancedSearch").dialog("open");
				return false;
			});
		},
		
		init:function() {
			var self = this;
			self.lightBoxEnt();
		}	
	},
	
	/*
	MODULE:ent 
	*/
	megaDropdownSelect: {
		// Code to submit URLs through the jQTransform select menus on the entertainment homepages.
		// Created by Bryan Robinson Nov. 9, 2010
		
		submitVals: function() {
			$(".searchGoButton").click( function(e) {
				e.preventDefault();
				var dropdownValue = $(this).parent().find("div.jqTransformSelectWrapper ul li a.selected").attr("title"); // Find the selected element of the dropdown and grab its title (where we store the original 'value')
				window.location = dropdownValue + "&CID=ent_mega_dropdowns"; // Send the user to the search page with that query
			});
			
		},
		
		init: function() {
			var self = this;
			self.submitVals();
		}	
		
	},
	

	entMegaNav: {
	
		megaHoverOver: function() {
		    $(this).find(".sub").stop().fadeTo(0, 1).show();//Find sub and fade it in
		
		    (function($) {
		        //Function to calculate total width of all ul's
		        jQuery.fn.calcSubWidth = function() {
		            rowWidth = 0;
		            //Calculate row
		            $(this).find("ul").each(function() { //for each ul...
		                rowWidth += $(this).width(); //Add each ul's width together
		            });
		        };
		    })(jQuery); 

		   /* if ( $(this).find(".row").length > 0 ) { //If row exists...

		        var biggestRow = 0;	

		        $(this).find(".row").each(function() {	//for each row...
		            $(this).calcSubWidth(); //Call function to calculate width of all ul's
		            //Find biggest row
		            if(rowWidth > biggestRow) {
		                biggestRow = rowWidth;
		            }
		        });

		        $(this).find(".sub").css({'width' :biggestRow}); //Set width
		        $(this).find(".row:last").css({'margin':'0'});  //Kill last row's margin

		    } else { //If row does not exist...

		        $(this).calcSubWidth();  //Call function to calculate width of all ul's
		        $(this).find(".sub").css({'width' : rowWidth}); //Set Width

		    }*/
		},
		//On Hover Out
		megaHoverOut: function() {
		  $(this).find(".sub").stop().fadeTo(0, 0, function() { //Fade to 0 opactiy
		      $(this).hide();  //after fading, hide it
		  });
		},

		
		
		init: function() {
	
			//Set custom configurations
			var config = {
			     sensitivity: 2, // number = sensitivity threshold (must be 1 or higher)
			     interval: 0, // number = milliseconds for onMouseOver polling interval
			     over: this.megaHoverOver, // function = onMouseOver callback (REQUIRED)
			     timeout: 0, // number = milliseconds delay before onMouseOut
			     out: this.megaHoverOut // function = onMouseOut callback (REQUIRED)
			};

			$("ul#topnav li div.sub").css({'opacity':'0', 'display':'none'}); //Fade sub nav to 0 opacity on default
			$("ul#topnav li").hoverIntent(config); //Trigger Hover intent with custom configurations

			
			this.megaHoverOver();
			this.megaHoverOut();
		
		}
	},
	
	
	
	datePicker: {
		init: function() {
			var dates = $( "#id_start_date, #id_end_date" ).datepicker({
				changeMonth: true,
				numberOfMonths: 1,
				showOptions: {direction: 'down'},
				onSelect: function( selectedDate ) {
					var option = this.id == "id_start_date" ? "minDate" : "maxDate",
						instance = $( this ).data( "datepicker" );
						date = $.datepicker.parseDate(
							instance.settings.dateFormat ||
							$.datepicker._defaults.dateFormat,
							selectedDate, instance.settings );
					dates.not( this ).datepicker( "option", option, date );
				}
			});
	
	
			$("#id_event_date").datepicker({
				changeMonth: true,
				showOptions: {direction: 'down'}
			});	
		}
	},
	

	
	
	
	/*
	MODULE:news
	*/
	navigation: {
		/*
		Uses superfish plugin from http://users.tpg.com.au/j_birch/plugins/superfish/
		to handle dropdown menus in site-wide navigation
		*/
		superfish: function() {
			// Class name to be applied to active nav. Used in superfish() init
			var path = 'current';

			// Parse the class chain on #site_nav and match "nav_xxxx" where "xxxx" is the section name (e.g. news, sports, business)
			var navContainer = $('ul#site_nav');
			
			if (navContainer.length) {
				var current_nav = navContainer.attr('class').match(/nav_[a-z]*/); // match() returns an array

				// If the value for the current nav is "nav_" or null (i.e. something broke) then default to nav_news
				if (current_nav == null || current_nav[0] == 'nav_') {
					if (jSINGconf.theme.SITE_ABBR == 'oaw' || jSINGconf.theme.SITE_ABBR == 'gve') {
						//default to nav_football if site is orangeandwhite or govolsxtra
						$('ul#site_nav li#nav_football').addClass(path);
						$('ul#site_nav li#nav_football > ul.secondary_nav').css('display', 'block'); // Fixes bug
					}
					else {
						$('ul#site_nav li#nav_news').addClass(path);
					}
				}
				else {
					$('ul#site_nav li#' + current_nav[0]).addClass(path);
					$('ul#site_nav li#' + current_nav[0] + ' > ul.secondary_nav').css('display', 'block'); // Works around an IE7 bug
				}

				// Initialize superfish
				$('ul#site_nav').superfish({ 
					pathClass: path,
					hoverClass: 'hover',
					pathLevels: 2,
					delay: 1500,
					speed: 'fastest',
					autoArrows: true,
					dropShadows: false
				});
			}
		},
		/*
		Positions tertiary and quaternary navigation dropdowns based on their proximity the right edge of the 
		page container.	 Assumes the page container is 994px wide and nav drop downs are 150px wide.

		Tertiary nav: applying .shift_left will cause the drop down to align it's right edge with the right edge
		of the parent category.

		Quaternary nav: applying .pop_left will cause the nav to pop to the left side of the tertiary nav instead
		of the default right side.
		*/
		navPosition: function () {
			var wrapper_offset = $("#page_wrapper").offset(),
				nav_offset = 844 + wrapper_offset.left;

			// If the tertiary nav will overlap the right edge of the wrapper div, apply .shift_left
			$("ul.tertiary_nav").each(function() {
			   var offset = $(this).offset();
			   if (offset.left > nav_offset) {
				   $(this).addClass("shift_left");
			   } 
			});

			// If the quaternary nav will overlap the right edge of the wrapper div, apply .pop_left
			$("ul.quaternary_nav").each(function() {
				var offset = $(this).offset();
				if (offset.left > nav_offset) {
					$(this).addClass("pop_left");
			   }
			});
		},
		
		init: function() {
			this.navPosition();
			this.superfish();
		}
	},
	/*
	MODULE:news (potentially condense into core later)
	*/
	login: {
		globalFormToggle: function() {
			var self = this;
			
			self.loginLink.click(function(e) {
				e.preventDefault();
				self.globalSiteLogin.toggle();
			});
		},
		globalFormClose: function() {
			var self = this;
			
			self.closeLink.click(function(e) {
				e.preventDefault();
				self.globalSiteLogin.hide();
			});
		},
		showGlobalLoadingIndicator: function() {
			var self = this,
				f = self.loginForm;
				
			self.loader.hide();
				
			// Hide the form, Insert a loading indicator
			f.fadeOut(200, function() {
				self.loader.insertAfter(f).fadeIn(200);
			});
			
		},
		hideGlobalLoadingIndicator: function() {
			var self = this;
			self.loader.fadeOut(200, function() {
				self.loginForm.fadeIn(200);
			});
		},
		showGlobalLoginError: function () {
			var self = this;
				$('<div id="login_error">Please enter a correct username and password. Note that both fields are case-sensitive.</div>').appendTo(self.loginForm);
		},
		globalLoginSuccess: function() {
			/*
			If the user is logging in from the logout page, 
			redirect to homepage. Otherwise, reload the existing page
			*/
			if (window.location.pathname == "/accounts/logout/") {
				window.location = '/'; // Redirect to the homepage
			} else {
				window.location.reload();
			}
		},
		globalAjaxSubmit: function() {
			var self = this,
				f = self.loginForm;
			
			f.submit(function(e) {
				e.preventDefault();
				
				// Show loading indicator 
				self.showGlobalLoadingIndicator();
				
				// Make POST request to login
				$.ajax({
					type: "POST",
					url: self.loginFormAjaxAction,
					data: f.serialize(),
					success: function(results){
						/*
						Whether the login was good or bad, the server will return a successful response.
						We'll check the response for glenn's success/failure indicators
						and proceed accordingly. An XML document is returned with a <result></result> value.
						If the result contains 'wiggity' it's successful.
						If the result contains 'whack' it's a failure
						*/
						
						if (results.search('wiggidy') != -1) {
							/* Success
								Reload the current page to complete the process
							*/
							self.globalLoginSuccess();
						} else {
							/* Failure
								Replace loading gif with fields and error message
							*/
							// TODO: Replace loading gif with fields and error message
							self.hideGlobalLoadingIndicator();
							self.showGlobalLoginError();
						}
					}
				});
			});
		},
		config: function() {
			var self = this;
			self.loginLink = $("#site_services_login");
			self.closeLink = $("#global_login_close");
			self.loginForm = $('#loginform1');
			self.loginFormAction = self.loginForm.attr('action');
			self.loginFormAjaxAction = self.loginFormAction.replace('/login/', '/ajax_login/');
			self.loader = $('<div id="global_login_loading"><img src="' + jSINGconf.theme.CORP_MEDIA_URL + '/img/ajax-loader-video.gif" alt="Logging in" /><span class="logging_in">Logging in...</span></div>');
		},
		init: function() {
			var self = this;
			self.globalSiteLogin = $("#global_site_login");
			
			if (self.globalSiteLogin.length) {
				self.config();
				self.globalFormToggle();
				self.globalAjaxSubmit();
				self.globalFormClose();	
			}
		}
	},
	
	/*
	MODULE:mixed
	*/
	carousels: { // FUNCTIONAL, COULD USE OPTIMIZATION 
		/*
		MODULE:core
		*/
		utils: {
			/**
			 * Pause autoscrolling if the user moves with the cursor over the clip. Used by all carousels with autoscroll
			 * @method pauseAutoScrollOnHover
			 * @param {jQuery carousel object} carousel The carousel passed by jCarousel
			 * Modified by Ryan Berg October 21, 2009
			 */
			pauseAutoScrollOnHover: function(carousel) {
				// 
				carousel.clip.hover(function() {
					carousel.stopAuto();
				}, function() {
					carousel.startAuto();
				});
			}
		},
		
		/*
		MODULE:news
		*/
		alertBarCarousel: {
			doAlertBarCarousel: function(carousel) {
				var controls = "<div id='alert_bar_controls'><a href='#alert_bar_carousel' class='alert_bar_control alert_bar_prev'>Previous</a><div id='alert_bar_counter'><span id='alert_bar_counter_current'>1</span> of <span id='alert_bar_counter_total'>" + carousel.size() + "</span></div><a href='#alert_bar_carousel' class='alert_bar_control alert_bar_next'>Next</a></div>";

				$('#alert_bar_clipper').prepend(controls);

				// Disable autoscrolling if the user clicks the prev or next button.
				$('#alert_bar_controls a.alert_bar_prev').bind('click', function() {
					carousel.prev();
					carousel.startAuto(0);
					return false;
				});

				$('#alert_bar_controls a.alert_bar_next').bind('click', function() {
					carousel.next();
					carousel.startAuto(0);
					return false;
				});
				
				jSING.carousels.utils.pauseAutoScrollOnHover(carousel);
				
			},
			alertBarOnSlide: function(carousel, item, current_page, state) {
				$("#alert_bar_counter_current").text(current_page);
			},
			init: function() {
				var alertBar = $("#alert_bar_carousel");
				if (alertBar.children().size() > 1) {
					alertBar.jcarousel({
						vertical: true,
						auto: 6,
						animation: 250,
						easing: 'swing',
						scroll: 1,
						wrap: 'both',
						initCallback: this.doAlertBarCarousel,
						itemFirstInCallback: this.alertBarOnSlide,
						buttonNextHTML: null,
						buttonPrevHTML: null
					});
				}
			}
		},
		
		/*
		MODULE:news
		*/
		skyboxCarousel: {
			doSkyboxCarousel: function(carousel) {
				var pages = $('#skybox_carousel').children().size(); // Number of pages (displays max of 3 skyboxes per page)

				// Create "next" and "previous" controls
				var controls_list = $('#skybox_controls ul'),
					controls = "<a href='#skybox_carousel' class='carousel_control carousel_prev' title='See previous group of skyboxes'>Previous</a><a href='#skybox_carousel' class='carousel_control carousel_next' title='See next group of skyboxes'>Next</a>";
				controls_list.after(controls);

				// Create pagination controls
				for(i=1; i <= pages; i++) {
					controls_list.append("<li><a href='#skybox_wrapper' id='page" + i + "' title='Jump to Skybox page " + i + "'>" + i + "</a></li>");
				}

				// Set first page indicator to current
				$('#skybox_controls ul li:first-child').addClass('current');

				// Bind click function to pagination controls
				$('#skybox_controls li').bind('click', function() {
					carousel.scroll($.jcarousel.intval($(this).text()));
					$('#skybox_controls li').each(function() {
						if ($(this).hasClass('current')) {
							$(this).removeClass('current');
						}
					});
					$(this).toggleClass('current');
					return false;
				});

				// Bind click function to "next" button
				$('#skybox_controls .carousel_next').bind('click', function() {
					carousel.next();
					jSING.apptap.execAppTapEvent(this, "Event64", false);
					return false;
				});

				// Bind click function to "previous" button
				$('#skybox_controls .carousel_prev').bind('click', function() {
					carousel.prev();
					jSING.apptap.execAppTapEvent(this, "Event64", false);
					return false;
				});
			},
			skyboxOnSlide: function(carousel, item, current_page, state) {
				$('#skybox_controls ul li a').each(function() {
					if ($(this).attr('id') == 'page' + current_page) {
						$(this).parent().addClass('current');
					}
					else {
						$(this).parent().removeClass('current');
					}
				});
			},
			init: function() {
				var skyboxes = $('#skybox_carousel');
				if (skyboxes.children().size() > 1) {
					skyboxes.jcarousel({
						initCallback: this.doSkyboxCarousel,
						scroll: 1,
						wrap: "both",
						buttonNextHTML: null,
						buttonPrevHTML: null,
						itemFirstInCallback: this.skyboxOnSlide
					});
				}
			}
			
		},
		
		/*
		MODULE:news
		*/
		posterCarousel: {
			doPosterCarousel: function(carousel) {
				var pages = $('#poster').children().size(),
					controls_list = $('#poster_controls ul'),
					controls = "<a href='#poster' class='carousel_control carousel_prev' title='See previous poster content'>Previous</a><a href='#poster' class='carousel_control carousel_next' title='See next poster content'>Next</a>";

				controls_list.after(controls);

				// Create pagination controls
				for(i=1; i <= pages; i++) {
					controls_list.append("<li><a href='#poster' id='page" + i + "' + title='See poster page: " + i + "'>" + i + "</a></li>");
				}

				// Set first page indicator to current
				$('#poster_controls ul li:first-child').addClass('current');

				// Bind click function to pagination controls
				$('#poster_controls li').bind('click', function() {
					carousel.scroll($.jcarousel.intval($(this).text()));
					$('#poster_controls li').each(function() {
						if ($(this).hasClass('current')) {
							$(this).removeClass('current');
						}
					});
					$(this).toggleClass('current');
					carousel.startAuto(0);
					return false;
				});

				// Disable autoscrolling if the user clicks the prev or next button.
				$('#poster_controls a.carousel_prev').bind('click', function() {
					carousel.prev();
					carousel.startAuto(0);
					jSING.apptap.execAppTapEvent(this, "Event65", false);
					return false;
				});

				$('#poster_controls a.carousel_next').bind('click', function() {
					carousel.next();
					carousel.startAuto(0);
					jSING.apptap.execAppTapEvent(this, "Event65", false);
					return false;
				});

				jSING.carousels.utils.pauseAutoScrollOnHover(carousel);
			},
			posterOnSlide: function(carousel, item, current_page, state) {
				$('#poster_controls ul li a').each(function() {
					if ($(this).attr('id') == 'page' + current_page) {
						$(this).parent().addClass('current');
					}
					else {
						$(this).parent().removeClass('current');
					}
				});
			},
			init: function() {
				var poster = $('#poster');
				if (poster.children().size() > 1) {
					poster.jcarousel({
						auto: 8,
						animation: 250,
						easing: 'linear',
						scroll: 1,
						wrap: 'both',
						initCallback: this.doPosterCarousel,
						itemFirstInCallback: this.posterOnSlide,
						buttonNextHTML: null,
						buttonPrevHTML: null
					});
				}
			}
		},
		
		/*
		MODULE:core
		*/
		inlineCarousels: {
			doInlineGalleries: function(carousel) {
				var re = /\d+/,
					photoGallery = carousel.container.parent('.inline_carousel_wrapper'),
					photoGalleryWrapper = photoGallery.parents('.inline_carousel').eq(0),
					photoGalleryUL = photoGallery.find("ul.story_gallery").eq(0),
					photoGalleryItemsTotal = re.exec(photoGalleryUL.attr('class'))[0],
					id = re.exec(photoGallery.attr("id"))[0];

				// Insert controls into DOM
				if (photoGallery.hasClass("fullwidth")) {
					var inlineControls = "<div class='gallery_controls'><a href='#'	 id='gallery_" + id + "_prev' class='carousel_control carousel_prev'>Previous</a><a href='#' id='gallery_" + id + "_next' class='carousel_control carousel_next'>Next</a></div>",
					inlineControlsPredecessor = photoGalleryWrapper.find("h4").eq(0);
				} else {
					var inlineControls = "<div class='controls_wrapper'><div class='gallery_controls_sidebar'><a href='#'	 id='gallery_" + id + "_prev' class='carousel_control carousel_prev'>Previous</a> <span class='gallery_index'><span class='gallery_index_current'>1</span> of <span class='gallery_index_total'>" + photoGalleryItemsTotal + "</span></span> <a href='#' id='gallery_" + id + "_next' class='carousel_control carousel_next'>Next</a></div></div>",
					inlineControlsPredecessor = photoGalleryUL;
				}
				inlineControlsPredecessor.after(inlineControls);

				// Bind click function to "next" button
				$("a#gallery_" + id + "_next").bind('click', function() {
					carousel.next();
					return false;
				});

				// Bind click function to "previous" button
				$("a#gallery_" + id + "_prev").bind('click', function() {
					carousel.prev();
					return false;
				});
			},
			inlineGalleryFirstInCallback: function(carousel, item, idx, state) {
				/* Called by jCarousel when gallery is scrolled
					Only the single width gallery has a count */
				if (!carousel.container.parent('.inline_carousel_wrapper').hasClass("fullwidth")) {
					if ($(item).hasClass('gallery_inline_prompt')) { idx = '--'; } // For prompt panel, show a '-' instead of a number
					carousel.container.find(".gallery_index_current").eq(0).html(idx);
				}
			},
			init: function() {
				var self = this,
					inlineCarousels = $('.inline_carousel');
				
				if (inlineCarousels.length) {
					// Use jQuery to handle hover opacity of prompts to handle IE without extra code
					var galleryPrompts = $('.gallery_inline_prompt a');
					galleryPrompts.css({ opacity: .8 });
					galleryPrompts.hover(function() {
						$(this).css({ opacity: .9});
					}, function() {
						$(this).css({ opacity: .8});
					});
					
					$(".inline_carousel_wrapper ul.story_gallery").each(function() {
						var photoGallery = $(this);
						if (photoGallery.parents('.story_gallery_wrapper').eq(0).hasClass("fullwidth")) {
							var scrollNum = 4;
						} else {
							var scrollNum = 1;
						}
						photoGallery.jcarousel({
							initCallback: self.doInlineGalleries,
							animation: "fast",
							easing: "linear",
							scroll: scrollNum,
							wrap: "both",
							itemFirstInCallback: self.inlineGalleryFirstInCallback,
							buttonNextHTML: null,
							buttonPrevHTML: null
						});
					});
				}
			}
		},
		
		init: function() {
			var self = this;
			self.alertBarCarousel.init(); // Breaking news alerts
			self.skyboxCarousel.init(); // Skyboxes atop homepage, bottom of all others
			self.posterCarousel.init(); // Posters
			self.inlineCarousels.init(); // Inline galleries. There can be more than one on a page
		}
	},
	/*
	Tabs plugin comes from jQuery UI
	MODULE:core
	*/
	tabs: {
		doTabs: function() {
			$(".tab_wrapper").tabs();
		},
		ieClearReorderArrows: function(getPathing) {  //removes the up/down arrows (for reordering the buckets) after a new tab has been clicked, for IE7 issue		
			$(".combined_sections_bucket_tabs li").children("a").click(function() {
				$("#combined_sections .bucket").children("a.reorder").remove();
			});
		},
		init: function() {
			var self = this;
			self.doTabs();
			self.ieClearReorderArrows();
		}
	},
	
	

	/*
	MODULE:core (drives searching across news and ent for events, restaurants, etc)
	*/
	ajax: {
		/**
		 * Create query string from value of each <select> field
		 * @method createQuery
		 * @param {jQuery object} selectField The form's select boxes
		 * @return {String} query string for ajax request
		 * Modified by Ryan Berg October 21, 2009
		 */
		createQuery: function(selectField) {
			var query = "";
			
			// If there has been a search, make sure that sticks in the query string
			var requestQuery = jSING.utils.getUrlAttr('q');
			if (requestQuery) {
				query += 'q=' + requestQuery + '\&';
			}
			
			selectField.each(function(i){
				if ($(this).val() != "") {
					query += $(this).attr("name") + "=" + $(this).val() + "\&";
				}
			});
			return query.substring(0, query.length-1); // Strip off the last "&" to avoid IE6 bug
		},
		searchFilter: function(url) {
			var self = this;
			
//			jSING.maps.handleMapLinks(); // Handle all yahoo map links
			$("input#search_button").remove(); // Remove the search button
			var selectField = $("#browse_search_form .vSelectField"),
				loadingDiv = $("#main_loader"),
				pageLinks = $("#results_list li ul.paginated_arrows li a");

			loadingDiv.ajaxStart(function() {
				$(this).show().next().fadeTo("medium", 0.25);
			});

			loadingDiv.ajaxStop(function() {
				$(this).hide().next().fadeTo("medium", 1);
			});

			self.createQuery(selectField);

			selectField.change(function () {
				var query = self.createQuery(selectField); // Create query string from <select> fields

				// Submit query string to url passed in from template and insert results into DOM
				$.ajax({
					type: "GET",
					url: url,
					data: query,
					dataType: "html",
					success: function(results){
//						yahooMapList = new Array(); // Reset the global list of yahoo map links
						$("ul#results_list.browse_list").html(results);
//						jSING.maps.handleMapLinks(); // Handle all new yahoo map links
					}
				});
			});

			pageLinks.live("click", function(){
				var query = self.createQuery(selectField); // Create query string from <select> fields
				query += "&page=" + $(this).attr("id"); // Append page number to query string

				// Submit query string to url passed in from template and insert results into DOM
				$.ajax({
					type: "GET",
					url: url,
					data: query,
					dataType: "html",
					success: function(results){
//						yahooMapList = new Array(); // Reset the global list of yahoo map links
						$("ul#results_list.browse_list").html(results);
//						jSING.maps.handleMapLinks(); // Handle all new yahoo map links
					}
				});
				return false;
			});
		}
	},
	
	/*
	MODULE:core (drives searching across news and ent for events, restaurants, etc)
	*/
	calendars: {
		config: {
			ajaxMonthURL: "/calendar/event/_async_month_calendar/"
		},
		/*
		A wrapper around jQuery's ajax call for our calendars
		*/
		ajaxRequest: function(url, query, resultsSelector, callback) {
			
			var self = this,
				callback = callback || function() { return null };
			/*
			if (loaderSelector != null) {
				loader = $(loaderSelector);
				loader.fadeIn("fast");
				$(resultsSelector).fadeTo("medium", 0.25);
			}
			*/
			
			$.ajax({
				type: "GET",
				url: url,
				data: query,
				dataType: "html",
				success: function(results){
					//jSINGconf.maps.yahooMapList = new Array(); // Reset the global list of yahoo map links
					$(resultsSelector).html(results); // Insert results into the wrapper div 
					/*
					if (loaderSelector != null) {
						loader.fadeOut("fast");
						$(resultsSelector).fadeTo("medium", 1);
					}
					*/
					callback();// Run the callback method if provided
				}
			});
		},
		
		/*
		Used for navigating months within a calendar
		*/
		monthNav: function(resultsSelector) {
			var self = this;
			$("td.month_nav a").click(function () {
				query = $(this).attr("value");
				
				self.ajaxRequest(self.config.ajaxMonthURL, query, resultsSelector, function() { self.monthNav(resultsSelector); });
				
				return false;
			});
		},
		
		/*
		Handle clicking of tabs in sidebar events widget
		*/
		calTabsSecondary: function(containerSelector, resultsSelector) {
			var self = this,
				query = "";
				containerSelector = containerSelector || "#sidebar_events", // Default for sidebar
				resultsSelector = resultsSelector || "#calendar_results", // Default for sidebar
				loader = $(containerSelector + "_loader");

			$(containerSelector + " li.cal_tab_last a").click(function () {
				loader.ajaxStart(function() {
					$(this).show();
					$(resultsSelector).fadeTo("medium", 0.25);
				});

				loader.ajaxStop(function() {
					$(this).hide();
					$(resultsSelector).fadeTo("medium", 1);
				});

				$(this).parent().siblings().removeClass("ui-tabs-selected"); // Remove active state from all other days
				$(this).parent().addClass("ui-tabs-selected"); // Set clicked day to be active
				
				self.ajaxRequest(self.config.ajaxMonthURL, '', resultsSelector, function() {self.monthNav(resultsSelector); });
				
				return false;
			});
		},
		
		/*
		Normalizes a form's field values into a query string.
		TODO: Look into using jquery's serialize functions?
		*/
		filterQuery: function() {
			// Creates a query string from the form select fields
			var query = "";

			// Get the date for the day tab that was clicked
			var date = $("#events_browse ul.cal_tab_header li.ui-tabs-selected:first").eq(0).children("a:first").attr("value");
			
			// Iterate through each drop-down and build query string
			$("#browse_search_form .vSelectField").each(function(i){
				var field = $(this),
					fieldVal = field.val();
					
				if (fieldVal != "") {
					if (query != "") {
						query += "&";
					}
					query += field.attr("name") + "=" + fieldVal;
				}
			});

			return query + "&start_date=" + date;
		},
		
		/*
		Used to handle filtering of event calendar
		*/
		handleCalendarSelect: function() {
			var self = this;
			$("#browse_search_form .vSelectField").change(function () {
				// Show/hide the loading graphics
				$("#filter_load").ajaxStart(function() {
					$(this).show();
					$(this).next().fadeTo("medium", 0.25);
				});

				$("#filter_load").ajaxStop(function() {
					$(this).hide();
					$(this).next().fadeTo("medium", 1);
				});

				query = self.filterQuery() + "&framing=event_filter_list";	// build query string from form fields

				self.ajaxRequest("/events/", query, "ul.browse_list");
			});
		},
		
		/* 
		Handle clicks on date tabs of the main Events browse/search page. Also handles filtering
		and AJAX requests for filtered event lists.
		*/
		calTabsPrimary: function() {
			var self = this,
				resultsSelector = "#events_browse .search_results_wrapper";
			//jSING.maps.init(); // Handle all yahoo map links
			var query;
			$("input#search_button").remove(); // Remove the search button for results filtering

			$("#events_browse ul.cal_tab_header li a").click(function () {
				$("#events_browse .ajax_loader").ajaxStart(function() {
					$(this).show();
					$(this).next().fadeTo("medium", 0.25);
				});

				$("#events_browse .ajax_loader").ajaxStop(function() {
					$(this).hide();
					$(this).next().fadeTo("medium", 1);
				});

				$(this).parent().siblings().removeClass("ui-tabs-selected"); // Remove active state from all other days
				$(this).parent().addClass("ui-tabs-selected"); // Set clicked day to be active

				if ($(this).hasClass("browse_cal")) {
					self.ajaxRequest(self.config.ajaxMonthURL, query, resultsSelector, function() {self.monthNav(resultsSelector); });
					
				} else {
					query = "start_date=" + $(this).attr("value") + "&framing=event_day_list"; // Create query based on the clicked day

					self.ajaxRequest('/events/', query, '#events_browse .search_results_wrapper', function() {self.handleCalendarSelect(); $("input#search_button").remove(); });
				}
				return false;
			});

			// Handle selections in form drop-downs - submit AJAX request with updated query each time a selection is made
			self.handleCalendarSelect();

			
		},
		/*
		Handles the entertainment sites' navigation calendar.
		*/
		calEntNav: function() {
			var self = this,
				selector = "#nav_calendar",
				resultsSelector = "#nav_calendar li";
			/* calendar load on page load */
			self.ajaxRequest(self.config.ajaxMonthURL, '', resultsSelector, function() {self.monthNav(resultsSelector); });
		},		
		init: function() {
			jSING.calendars.calTabsSecondary();
		}
	},
	
	/*
	MODULE:core
	*/
	comments: {
		firstLoad: true, // Certain functions are only run on page load. Once init runs, this variable is set to false

		commentsContainer: $("#comments.comments_enabled"), // Wraps around all comments stuff, including the titlebar
		commentsWrapper: $("#comment_wrapper"), // Wraps pagination and comments
		commentsListContainer: $("#comment_list"), // Contains the list of actual comments
		commentsAd: $("#comments_ad"),
		commentsLoader: $('<div id="comments_loader" style="position: absolute; width: 575px; margin: 14px auto; text-align: center; font-size: .75em; color: #666;"><img src="' + jSINGconf.theme.CORP_MEDIA_URL + '/img/ajax-loader.gif" alt="Loading comments" /><p>Loading comments</p></div>'),
		commentsPagination: $('.comments_paginated_arrows'),
		commentsPaginationContainers: $('.pagination_comments'),
		commentsTitlebar: $('#comments .titlebar:first'),
		commentsCountStorage: $('.comment_count_storage'),
		commentsPageSelectors: $('.comments_pages_select'),
//		commentsList: $('.comment_wrapper'),
//		commentsFormWrapper: $('.comment_list_wrapper')
/*		
		handlePagination: {
			arrowsClick: function() {
				// Capture clicks on next/previous links
				$('.comments_paginated_arrows li a').live('click', function(e) {
					e.preventDefault();
					jSING.comments.loadCommentsData(this.href.replace('/list/', '/json/'));
				});
			},
			selectChange: function() {
				// Capture selection via dropdown menu
				$("select.comments_pages_select").live('change', function() {
					jSING.comments.loadCommentsData($(this).val());
				});
			},
			init: function() {
				var self = this;
				
				self.arrowsClick();
				self.selectChange();
				
				// Get the loader ready
				self.commentsLoader.hide().insertBefore(self.commentsListContainer);
			}
		},
*/
		handlePagination: function() {
			var self = this;

			// Capture clicks on next/previous links
			$('.comments_paginated_arrows li a').live('click', function(e) {
				e.preventDefault();
				self.loadCommentsData(this.href.replace('/list/', '/json/'));
			});

			// Capture selection via dropdown menu
			$("select.comments_pages_select").live('change', function() {
				if (!self.firstLoad) {
					self.loadCommentsData($(this).val());
				}
			});

			// Get the loader ready
			self.commentsLoader.hide().insertBefore(self.commentsListContainer);
		},
		
		updatePagination: function(paginationHTML) {
			var self = this;
			self.commentsPaginationContainers.each(function() {
				$(this).html(paginationHTML);
			});
			//self.handlePagination.selectChange();
		},
		updateCommentCounts: function(destinationSelector, commentCount) {
			var destinationSelector = destinationSelector || ".dynamic_comment_count",
				commentCount = commentCount || $("#comment_wrapper .comment_list_wrapper:first").attr("id").match(/\d+/)[0] * 1;
			/*
			if ($('#titlebar_comment_count').length == 0) {
				// Comment count is not yet in the titlebar
				var titlebarCommentCount = ' <span id="titlebar_comment_count">&raquo; <span class="' + destinationSelector.replace(".","") + '"></span></span>';
				$("#comments .titlebar:first h3:first").append(titlebarCommentCount); // Add comment count container to the titlebar
			}
			*/
			$(destinationSelector).each(function() {
				$(this).text(commentCount); // Update all dynamic comment counts
			});
		},
		loadCommentsData: function(commentsURL) {
			/*
			The JSON response is rendered by the comments/paginated_list_json.html template
			*/
			var self = this;

			$.scrollTo(self.commentsContainer, 200); // Scroll to the comments div

			// Fade comments out, put loader on top
			self.commentsListContainer.fadeTo(100, .2, function() {
				self.commentsLoader.fadeIn(200);

				$.getJSON(commentsURL, function(results) {
					/*
					When loading is complete, do all the hard work
					*/

					// Remove newlines and tabs from the rendered data so that it'll be safe for JSON parsing
					var renderedData = results.rendered_data.replace(self.whitespaceRegEx, '');

					self.changePage($.parseJSON(renderedData));
				});
			});

		},
		removeComments: function() {
			/*
			Remove the comments while leaving the ad in place
			*/
			$('.comment_wrapper').remove();
		},
		renderComments: function(commentList) {
			/*
			Render the comments around an ad if it exists
			*/
			var self = this,
				commentsCount = commentList.length,
				commentsBeforeAdHTML = '',
				commentsAfterAdHTML = '';

			if (self.commentsAdIndex != -1) {
				// There is an ad. Use its index as the breakpoint
				var hasAd = true,
					breakpoint = self.commentsAdIndex;
			} else {
				// There is no ad, so break at the end
				var hasAd = false,
					breakpoint = commentsCount;
			}


			// Loop from the beginning to the breakpoint, which is an ad if it exists, if not it's the end
			for (var i = 0; i < breakpoint; i++) {
				commentsBeforeAdHTML += commentList[i];
			}

			self.removeComments();

			// Insert the comments at the beginning of the comments list
			var commentsBeforeAd = $(commentsBeforeAdHTML);
			commentsBeforeAd.prependTo(self.commentsListContainer);

			// If there's an ad, loop from the breakpoint to the end
			if (hasAd) {
				for (var i = breakpoint; i < commentsCount; i++) {
					commentsAfterAdHTML += commentList[i];
				}
				// Insert the comments at the end of the comments list
				var commentsAfterAd = $(commentsAfterAdHTML);
				commentsAfterAd.appendTo(self.commentsListContainer);
			}

			// Fade comments in, remove loader
			self.commentsLoader.fadeOut(100, function() {
				self.commentsListContainer.fadeTo(100, 1);

			});


		},
		changePage: function(commentsData) {
			var self = this;

			self.renderComments(commentsData.commentList);
			self.updatePagination(commentsData.paginationHTML);
			self.updateCommentCounts(null, commentsData.commentCount);
		},
		getComment: function() {
			/*
			Specific comments can be linked to in the URL using:
			?comments_id=12345 (From {{ comment.get_absolute_url }}), PREFERRED METHOD
			or
			#c12345 (Anchor of the comment on the page)
			*/
			fromGet = function() {
				return jSING.utils.getUrlAttr('comments_id');
			}
			fromHash = function() {
				if (window.location.hash) {
					var re_exec = /c[\d]+/.exec(window.location.hash);
					if (re_exec) {
						return re_exec[0].replace('c', '');
					} else {
						return null;
					}
				} else {
					return null;
				}

			}
			return function() {
				/*
				If a comment is specified, scroll to it
				*/
				var self = this,
					commentId = fromGet() || fromHash() || null;

				if (commentId) {
					$.scrollTo($("#c" + commentId), 200);
				}
			}();
		},
		initNonPublicCommentLinks: function() {
			/* Reveals hidden flagged comments by clicking on a link */
			$("p.comment-not-public-warning a").live('click', function(event) {
				event.preventDefault();
				var link = $(this),
					href = jSING.utils.getTargetID(link),
					commentDiv = $(href);
				// Fade out the warning then fade in the comment
				link.parent().fadeOut("fast", function() {
					commentDiv.fadeIn();
				});
			});
		},
		hideComments: function(setCookie) {
			var self = this,
				setCookie = setCookie || false;
				
			self.commentsContainer.addClass('hidecomments');
			self.commentsToggleLink.text("Show"); // Change the text of the link to Show
			self.commentsWanted = false;
			
			// Only set the cookie if the 'hide' button was clicked
			if (setCookie) {
				$.cookie("hidecomments", 'out', { expires: 730, path: '/' }); // Set the cookie to hide comments
			}
			
		},
		showComments: function() {
			var self = this;
			self.commentsContainer.removeClass('hidecomments');
			self.commentsToggleLink.text("Hide"); // Change the text of the link to Hide
			self.commentsWanted = true;
			$.cookie("hidecomments", null, { path: '/' }); // Delete the font size cookie if it exists
		},
		handleCommentsToggle: function() {
			var self = this;

			self.commentsToggleLink.click(function(event) {
				// Handle the click event of the show/hide link
				event.preventDefault();
				if (self.commentsToggleLink.text() == "Hide") {
					self.hideComments(true);

				} else {
					self.showComments();
				}
			});
		},
		getCommentsVisibilityCookie: function() {
			if ($.cookie("hidecomments") && $.cookie("hidecomments") == "out") {
				return false;
			}
			return true;
		},
		hideUnwantedComments: function() {
			var self = this;

			if (!self.commentsWanted) {
				self.hideComments();
			}
		},
		insertToggleLink: function() {
			var self = this;

			if (self.commentsWanted) {
				var toggleText = "Hide";
			} else {
				var toggleText = "Show";
			}
			self.commentsToggleUL = $('<ul class="deeplinks"><li><a href="#comments" id="comments_toggle">' + toggleText + '</a></li></ul>');
			self.commentsToggleUL.appendTo(self.commentsTitlebar);
			self.commentsToggleLink = $('#comments_toggle');
		},
		selectInputRefresh: function() {
			/*
			Fixes a problem where after selecting any page of comments but the first,
			reloading the page sets the comment lists back to page 0, 
			but shows the previously selected page as selected
			*/
			var self = this;

			self.commentsPageSelectors.each(function() {
				var select = $(this),
					selected = select.children(':selected:first'),
					selectedIndex = selected.index();

				if (selectedIndex == 0) {
					select.trigger('change');
				}
			});
		},
		init: function() {
			var self = this;

			if (self.commentsContainer.length) {
				self.commentsWanted = self.getCommentsVisibilityCookie();
				self.selectInputRefresh();
				self.insertToggleLink();
				self.hideUnwantedComments();
//				self.updateCommentCounts();
				self.whitespaceRegEx = /[\t\r\n]/g; // Used to clean out JSON responses

				// Get the ad index once and never do it again
				self.commentsAdIndex = self.commentsAd.index();

				self.handlePagination();

				// Scroll to a specified comment
				if (self.firstLoad) {
					self.getComment();
				}

				self.initNonPublicCommentLinks();
				self.handleCommentsToggle();
				self.firstLoad = false;
			}
		}
	},

	/*
	MODULE:core
	*/
	maps: {
		handleStaticMaps: function() {
			var self = this,
				staticMaps = $('.mapwindow_static'),
				re = /\d+/;
			
			if (staticMaps.length) {
				staticMaps.each(function() {
					var staticMap = $(this),
						mapID = re.exec(staticMap.attr("id"))[0],
						yahooMapInfo = jSINGconf.maps.yahooMapList[mapID];
						
					self.yahooMap(yahooMapInfo['id'], yahooMapInfo['lat'], yahooMapInfo['long'], yahooMapInfo['name'], yahooMapInfo['address'], yahooMapInfo['city'], yahooMapInfo['state'], yahooMapInfo['width'], yahooMapInfo['height']);
				});
			}
		},
		handleMapLinks: function() {
			var self = this,
				mapLinks = $('.browse_list_maplink'),
				loaderImage = jSINGconf.ajax.loaderImageURL,
				loader = $('<div class="ajax_loader" style="display: block; height: 32px;"><img src="' + loaderImage + '" alt="Loading map" width="32" height="32" /></div>'),
				re = /\d+/;
				
			mapLinks.live("click", function(e) {
				e.preventDefault(); // Keep the link from going through
				
				var mapLink = $(this),
					mapID = re.exec(mapLink.attr("id"))[0];
					
				var yahooMapDiv = $('#mapwindow_' + mapID);
				if (!yahooMapDiv.length) {
					// Map is not yet created, so create the map
					var yahooMapLink = jSINGconf.maps.yahooMapList[mapID], // Get the map info from the global variable
						yahooMapDiv = $('<div id="mapwindow_' + mapID + '" class="list_map"></div>'); // Create the map div object
					
					yahooMapDiv.append(loader); // Put the loading gif into the mapdiv
					mapLink.after(yahooMapDiv); // Insert the mapdiv after the link
					
					self.yahooMap(yahooMapLink['id'], yahooMapLink['lat'], yahooMapLink['long'], yahooMapLink['name'], yahooMapLink['address'], yahooMapLink['city'], yahooMapLink['state'], yahooMapLink['width'], yahooMapLink['height']);
					
					loader.remove(); // Take the loader out since the map has loaded now
				}
				
				yahooMapDiv.slideToggle();
				
			});
			
		},
		
		yahooMap: function(id, latitude, longitude, name, address, city, zip, width, height) {
			// Create a map object
			var mapsize = new YSize(width, height),
				mapDiv = document.getElementById('mapwindow_' + id),
				map = new YMap(mapDiv, YAHOO_MAP_REG, mapsize);

			// Add map controls
			map.addZoomShort();
			map.disableKeyControls();
			map.removeZoomScale(); 

			// Create geopoint and marker
			var geopoint = new YGeoPoint(latitude, longitude),
				newMarker = new YMarker(geopoint);

			// Insert address into marker
			var markerMarkup = "<div style='padding: 3px 7px; font-family: Verdana; font-size: 0.8em; width: 175px;'><strong>" + name + "</strong><br/>" + address +"<br/>" + city + " " + zip + "</div>";

			// Bind marker to click event
			YEvent.Capture(newMarker, EventsList.MouseClick,
				function(){
				   newMarker.openSmartWindow(markerMarkup) ;
				});

			// Add marker to map, center on marker and set zoom to 5
			map.addOverlay(newMarker);
			map.drawZoomAndCenter(geopoint, 5);
		},
		
		init: function() {
			var self = this;
			self.handleStaticMaps();
			self.handleMapLinks();
		}
	},
	
	/* MODULE: ent */
	neighborhoodMaps: {
		handleNeighborhoodMaps: function() {
			var self = this,				
				loaderImage = jSINGconf.ajax.loaderImageURL,
				loader = $('<div class="ajax_loader" style="display: block; height: 32px;position:absolute;top:150px;left:290px;z-index:0;"><img src="' + loaderImage + '" alt="Loading map" width="32" height="32" /></div>'),
				re = /\d+/;
							
			$.each(jSINGconf.maps.yahooMapList['placeMaps'], function(key, value) { // Loops through the top-level array (For placetype maps)
				var mapLinks = $('#neighborhood_maplink_' + key);
				
				$(document).ready(function() { // Similar to above, but loads on page load instead of click. Pulls data from first piece of array
						var yahooMapDiv = $('#mapwindow_0');
						if (!yahooMapDiv.length) {
							// Map is not yet created, so create the map
							yahooMapDiv = $('<div id="mapwindow_0" class="neighborhood_place_map"></div>'); // Create the map div object
							
							$("#neighborhood_map_bucket").append(yahooMapDiv); // Insert the mapdiv into #neighborhood_map_bucket
							yahooMapDiv.append(loader); // Put the loading gif into the mapdiv
							var mapWindowPlaceType = "mapwindow_0"; // Creates new var for yahooMapDiv to pass to the next class, since we've already changed it from above
							self.yahooMapNeighborhood(mapWindowPlaceType, value);

						}

						yahooMapDiv.show();
						$(".ajax_loader").hide(); // kill the loader that's on the page
				//		loader.remove(); // Take the loader out since the map has loaded now

					});
				
				
				mapLinks.live("click", function(e) {
					e.preventDefault(); // Keep the link from going through
					$(".ajax_loader").toggle(); // load the loader that's on the page
					var clickParent = $(this).parent();
					$(".active_neighborhood").removeClass('active_neighborhood');
					clickParent.addClass("active_neighborhood");
					clickParent.parent().parent().slideToggle('fast');
					if ($(".neighborhood_place_map").length) {
						$(".neighborhood_place_map").hide(); // Kills previous or default map on page before going through
					}
					var mapLink = $(this);
		//				mapID = re.exec(mapLink.attr("id"))[0];
					
					var yahooMapDiv = $('#mapwindow_' + key);
					if (!yahooMapDiv.length) {
						// Map is not yet created, so create the map
							yahooMapDiv = $('<div id="mapwindow_' + key + '" class="neighborhood_place_map"></div>'); // Create the map div object
					
						$("#neighborhood_map_bucket").append(yahooMapDiv); // Insert the mapdiv into #neighborhood_map_bucket
						yahooMapDiv.append(loader); // Put the loading gif into the mapdiv
						var mapWindowPlaceType = "mapwindow_" + key; // Creates new var for yahooMapDiv to pass to the next class, since we've already changed it from above
						self.yahooMapNeighborhood(mapWindowPlaceType, value);
					
					}
					
					$(".mapType").replaceWith("<span class='mapType'>" + $(this).html() + "</span>");
					
					yahooMapDiv.show("fast");

					$(".ajax_loader").toggle(); // kill the loader that's on the page
		//			loader.remove(); // Take the loader out since the map has loaded now
				
				});
				
			});
			
		},
		

		
		
		yahooMapNeighborhood: function(yahooMapDiv, placetypes) {
			// Create a map object
			
			var mapsize = new YSize(jSINGconf.maps.yahooMapList.placeMaps['neighborWidth'], jSINGconf.maps.yahooMapList.placeMaps['neighborHeight']),
				mapDiv = document.getElementById(yahooMapDiv),
				map = new YMap(mapDiv, YAHOO_MAP_REG, mapsize),
				zp = new YCoordPoint(10,50);



					// Add map controls
					map.addZoomShort(zp);
					map.disableKeyControls();
					map.removeZoomScale();
					
					
				
					$.each(placetypes, function(key, value) { // Loops through the arrays from the above class
						var geopoint = new YGeoPoint(value['lat'], value['long']),
						newMarker = new YMarker(geopoint, createCustomMarkerImage());
						// Insert address into marker
						if(!value['imgSrc']) {
							// If the template doesn't pass a get_lead_art_url 
							var markerMarkup = "<div class='smartWindowWrapper'><h3><a href='" + value['url'] + "'>" + value['name'] + "</a></h3><p class='smartWindowAddress'>" + value['address'] + "</p><p class='smartWindowUrl'><a href='" + value['url'] +"' title='More details about" + value['name'] + "'>More details &raquo;</a></p></div>";							
						}
						else {
							// If the template does pass get_lead_art_url tact an image into the smart window. w00t
							var markerMarkup = "<div class='smartWindowWrapper withPhoto'><div class='smartWindowText'><h3><a href='" + value['url'] + "'>" + value['name'] + "</a></h3><p class='smartWindowAddress'>" + value['address'] + "</p><p class='smartWindowUrl'><a href='" + value['url'] +"' title='More details about" + value['name'] + "'>More details &raquo;</a></p></div><div class='mapPhoto'><a href='" + value['url'] + "'><img src='" + value['imgSrc'] + "' /></a></div></div>";
						}
						
						
						newMarker.setSmartWindowColor("black");
						function createCustomMarkerImage(){  
							var myImage = new YImage();  
							myImage.src = "http://l.yimg.com/a/i/us/map/aj/451/mkr_blk_p1.gif";  
							myImage.size = new YSize(24,33);  
							myImage.offsetSmartWindow = new YCoordPoint(0,0);  
							return myImage;   
						};
						// Bind marker to click event
						YEvent.Capture(newMarker, EventsList.MouseClick,
							function(){
								newMarker.openSmartWindow(markerMarkup) ;
								map.panToLatLon(geopoint)
							});				

						
						// Adds marker to map
						map.addOverlay(newMarker);


					});

			map.drawZoomAndCenter(jSINGconf.maps.yahooMapList.placeMaps['neighborZip'], 5);
		},

		mapSortListToggle: function() {
			$(".mapFilter").live("click", function(e) {
				e.preventDefault();
				$(".map_sort_list").slideToggle('fast');	
			})
		},				

		
		init: function() {
			var self = this;
			self.handleNeighborhoodMaps();
			self.mapSortListToggle();
		}
	},
	
	
	/*
	MODULE:news
	*/
	reordering: { // COMPLETE, COULD USE NAMESPACE REFACTORING HOWEVER
		config: {
			reorderableItemsSelector: '',
			adIndex: 0,
			adSelector: '',
			reorderCookieName: '',
			containerSelector: ''
		},
		getNormalizedHeight: function(item, margin) {
			/*
			Attempts to compensate for browser differences regarding outerHeight values and margins
			margin should be the expected margin value, item should be a jquery object
			*/
			var h1 = item.outerHeight(true),
				h2 = item.outerHeight(false);
			if ( h1 == h2 ) { return h1 + margin; }
			return h1;
		},
		showReorder: function() {
			if (!preventReorder) {
				margin = 21;
				itemA = $(this); // The item hovered over
				itemAID = itemA.attr("id");
				itemAIndex = reorderableItems.index(itemA); // 0-based index in the list of all reorderable items
				itemAHeight = jSING.reordering.getNormalizedHeight(itemA, margin);
				itemAName = itemA.find(".titlebar h3").eq(0).text();

				function reorderItems(thisLink, direction) {
					preventReorder = true; // Don't let any other reordering events happen
					function swap(itemA, direction) {
						adCheck = 0;
						switch(direction) {
							case "up":
								itemBIndex = itemAIndex - 1;
								itemB = $(reorderableItems.get(itemBIndex));
								itemBYModifier = "+=";
								itemAYModifier = "-=";
								if (adIndex > 0) { adCheck = adIndex; }
								break;

							case "down":
								itemBIndex = itemAIndex + 1;
								itemB = $(reorderableItems.get(itemBIndex));
								itemBYModifier = "-=";
								itemAYModifier = "+=";
								if (adIndex > 0) { adCheck = adIndex - 1; }
								break;
						}
						itemBHeight = jSING.reordering.getNormalizedHeight(itemB, margin);
						//itemBY = itemB.offset().top;

						adHeight = 0;

						if (adIndex > 0 && itemAIndex == adCheck) {
							// The swap is going across the ad
							ad = $(adSelector);
							if (ad.length) {
								if (ad.length > 1) { ad = ad.eq(0); }
								adHeight = jSING.reordering.getNormalizedHeight(ad, margin);
								adY = ad.offset().top;
								adModifier = "";
							}
						}
						itemAY2 = itemAYModifier + (itemBHeight + adHeight).toString() + "px";
						itemBY2 = itemBYModifier + (itemAHeight + adHeight).toString() + "px";

						itemA.animate( { top: itemAY2 }, 400, "swing" ); // Move the clicked item
						itemB.animate( { top: itemBY2 }, 200, "swing" ); // Move the swapped item

						if (adHeight) {
							// Move the ad
							switch(direction) {
								case "up":
									adY2 = itemAHeight - itemBHeight;
									break;

								case "down":
									adY2 = itemBHeight - itemAHeight;
									break;
							}

							if (adY2 >= 0) {
								adModifier = "+=";
							} else if (adY2 < 0) {
								adY2 *= -1;
								adModifier = "-=";
							}

							adChange = adModifier + adY2.toString() + "px";
							ad.animate( { top: adChange }, 300, "swing" );
						}

						// Reorder the array
						reorderableItems[itemAIndex] = itemB[0];
						reorderableItems[itemBIndex] = itemA[0];

						// Write the cookie
						order = ""
						reorderableItems.each(function() {
							order = order + this.id + ",";
						});
						order = order.substring(0,order.length-1); // Ignore the last character, which is a comma
						$.cookie(reorderCookieName, order, { expires: 730 }); // Set the cookie for two years

						preventReorder = false; // Let other reordering events happen

						return reorderableItems;

					}
					/* THIS IS WHERE THE LOGIC ACTUALLY STARTS */
					itemA.children("a.reorder").remove(); // Ditch the arrows
					swap(itemA, direction);

					return reorderableItems;
				}

				function linkHTML(direction, itemAName) {
					return '<a href="#' + itemAID + '" id="reorder_' + direction + '_' + itemAID + '" class="reorder reorder_' + direction + '" title="Click to move ' + itemAName + ' ' + direction + ' on the page">Move ' + itemAName + ' '+ direction + '</a>'; // TODO determine the link text
				}

				function initArrow(direction, itemAName) {
					arrowHTML = linkHTML(direction, itemAName);
					arrow = $(arrowHTML).css("opacity", "0").click(
						function(e) {
							e.preventDefault(); // Stop from jumping to the link anchor
							jSING.apptap.execAppTapEvent(this, "Event63", false); // Execute these clicks with Apptap
							reorderItems($(this), direction); // Run the re
						}
					);
					switch(direction) {
						case "up":
							arrow.prependTo(itemA).css("opacity", "1");
							break;

						case "down":
							arrow.appendTo(itemA).css("opacity", "1");
							break;
					}

					//registerAppTapEvent("a.reorder", "Event63", false); // Register these clicks with Apptap 
				}

				$("a.reorder").remove(); // Remove any existing arrows first

				if (itemAIndex != 0) {
					// Not the first item, so create up arrow
					initArrow("up", itemAName);
				}
				if (itemAIndex != (reorderableItems.length - 1)) {
					// Not the last item, so create down arrow
					initArrow("down", itemAName);
				}
			}
			preventReorder = false;
		},
		hideReorder: function() {
			preventReorder = false; // Let other reordering events happen
			$(this).children("a.reorder").each(function() {
				reorderLink = $(this);
				reorderLink.animate({opacity: 0}, 100, function() {
					reorderLink.remove();
				});
			});
		},
		
		init: function(reorderableItemsSelector, containerSelector, adSelector, adIndex, reorderCookieName) {
			preventReorder = false; // Let things happen
			reorderableItemsSelector = reorderableItemsSelector; // Make global
			containerSelector = containerSelector; // Make global
			adSelector = adSelector; // Make global
			adIndex = adIndex; // Make global
			reorderCookieName = reorderCookieName; // Make global
			reorderableItems = $(reorderableItemsSelector); // Make global

			// If a cookie is set, reorder the items
			if ($.cookie(reorderCookieName)) {
				var itemIDs = $.cookie(reorderCookieName).split(",") || []; // Get the list of #ids
				var itemsList = reorderableItems; // Get the jquery object containing the items
				var itemsContainer = $(containerSelector).eq(0); // Get the jquery object with container of the items
				var itemsLength = itemIDs.length - 1;

				// If adIndex is 0, set a breakpoint at last item. Otherwise, break at the ad.
				if (!adIndex) {
					var breakPoint = itemsLength;
				} else {
					var breakPoint = adIndex - 1;
				}

				function cookieItemReorder(i, insertion) {
					var item = $("#" + itemIDs[i]); // Get each item ID by combining "#" with id from array
					if (item.length) {
						switch (insertion) {
							case "prepend":
								item.prependTo(itemsContainer);
								break;

							case "append":
								item.appendTo(itemsContainer);
								break;
						}
					}
				}
				// Loop until the breakPoint
				for (var i=breakPoint; i >= 0; i--) {
					cookieItemReorder(i, "prepend");
				}

				// Loop after breakPoint if there's an ad
				if (adIndex) {
					for (var i=breakPoint + 1; i <= itemsLength; i++) {
						cookieItemReorder(i, "append");
					}
				}

				reorderableItems = $(reorderableItemsSelector); // Reset the reorderable items list
			}

			var hoverConfig = {	   
				 sensitivity: 15, // number = sensitivity threshold (must be 1 or higher)	 
				 interval: 0, // number = milliseconds for onMouseOver polling interval	   
				 over: jSING.reordering.showReorder, // function = onMouseOver callback (REQUIRED)	  
				 timeout: 125, // number = milliseconds delay before onMouseOut	   
				 out: jSING.reordering.hideReorder // function = onMouseOut callback (REQUIRED)	   
			};

			reorderableItems.hoverIntent(hoverConfig);
		}
	},
	
	/*
	MODULE:core
	*/
	fonts: { // COMPLETE
		setFontSize: function(fontLinks, fontSizeName, link) {
			fontLinks.filter(".active").removeClass("active"); // Make any active font size links become inactive
			var deleteCookie = false,
				articleBody = $('#article_body');

			switch(fontSizeName) {
				case "small":
					articleBody.addClass("article_body_small").removeClass("article_body_large");
					break;

				case "large":
					articleBody.addClass("article_body_large").removeClass("article_body_small");
					break;

				default:
					articleBody.removeClass("article_body_small article_body_large");
					deleteCookie = true;
			}

			// Set the clicked font size link to active. link should be the jquery object of the clicked element. Will be null on init
			if (link) { link.addClass("active"); } else { $("a#font_" + fontSizeName).addClass('active'); }

			// Only default font size sets deleteCookie to true
			if (deleteCookie == true) {
				$.cookie("storyFontSizeName", null, { path: '/' }); // Delete font size cookie if it exists

			} else {
				$.cookie("storyFontSizeName", fontSizeName, { expires: 730, path: '/' }); // Set font size cookie for two years
			}
		},
		init: function() {
			// Get font size links. Only initiate if they exist
			var self = this,
				fontLinks = $(".story_tools .story_font a");
				
			if (fontLinks.length > 0) {
				// Get cookie value
				var fontSizeName = $.cookie("storyFontSizeName") || "default"; // Either small or large if present.

				// Set the initial font size
				self.setFontSize(fontLinks, fontSizeName, null); 

				// Handle clicks on all font buttons
				fontLinks.click(function(event) {
					event.preventDefault();
					var link = $(this),
						fontSizeName = link.attr("id").split("_")[1]; // IDs look like "font_default" "font_small" "font_large"
					self.setFontSize(fontLinks, fontSizeName, link);
				});
			}
		}
	},
	
	/*
	MODULE:core
	*/
	photos: {
		thumbnails: {
			/*
			When hovering over a thumbnail in a media_list,
			fade all of its siblings to 33% opacity
			*/
			hoverOpacity: function() {
				$('ul.media_list, ul.media_thumbs, ul.story_gallery').children().hover(function() {
					$(this).siblings().stop().fadeTo(100, 0.33);
				}, function() {
					$(this).siblings().stop().fadeTo(400, 1);
				});

			},
			init: function() {
				var self = this;

				self.hoverOpacity();
			}
		},
		
		sizeToggle: {
			toggle: null,
			item: null,
			itemSmall: null,
			itemLarge: null,
			
			handleToggle: function() {
				var self = this;
				
				self.toggle.click(function(e) {
					e.preventDefault();
					var t = $(this);
					
					if (!t.hasClass('loading_media')) {
						if (t.hasClass('see_larger')) {
							self.initSwap('larger');
						} else {
							self.initSwap('smaller');
						}
					}
					
				});
			},
			
			/*
			Lots of duplication, could be refactored a bit?
			*/
			initSwap: function(direction) {
				var self = this;
				
				switch(direction) {
					case "larger":
						if (!self.itemLarge) {
							self.itemLarge = self.item.clone();
							self.itemSmall = self.item.clone();
							
							self.item.fadeTo(300, .33);
							self.toggle.addClass('loading_media').html('Loading&hellip;');
							
							self.itemLarge.attr('src', self.itemLarge.attr('src').replace('_t300.', '_t607.')).load(function() {
								self.itemLarge.attr('width', '607').removeClass('media_tall');
								self.doSwap(direction);
							}).error(function() {
								self.toggle.removeClass('loading_media').html('Error:<br />The image could not be loaded. Please try again:<br />See smaller');
								self.itemLarge = null;
							});
							
						} else {
							self.doSwap(direction);
						}
						break;
						
					case "smaller":
						if (!self.itemSmall) {
							self.itemSmall = self.item.clone();
							self.itemLarge = self.item.clone();
							
							self.item.fadeTo(300, .33);
							self.toggle.html('Loading&hellip;');
							
							self.itemSmall.attr('src', self.itemSmall.attr('src').replace('_t607.', '_t300.')).load(function() {
								self.itemSmall.attr('width', '300').addClass('media_tall');
								self.doSwap(direction);
							}).error(function() {
								self.toggle.removeClass('loading_media').html('Error:<br />The image could not be loaded. Please try again:<br />See smaller');
								self.itemSmall = null;
							});
							
						} else {
							self.doSwap(direction);
						}
						break;
				}
				
				
			},
			
			doSwap: function(direction) {
				var self = this,
					oldItem = self.item;
				
				switch(direction) {
					case "larger":
						var newItem = self.itemLarge.clone();
						self.toggle.attr('class', 'see_smaller').html('See smaller');
						break;
					
					case "smaller":
						var newItem = self.itemSmall.clone();
						self.toggle.attr('class', 'see_larger').html('See larger');
						break;
				}
				self.toggle.removeClass('loading_media');
				oldItem.replaceWith(newItem);
				self.item = newItem;
			},
			
			init: function() {
				var self = this;
				self.toggle = $('#media_size_toggle'); // The link that toggles the size
				
				/* If the link exists, we'll handle its click */
				if (self.toggle.length) {
					self.item = $('#media');
					self.handleToggle();
				}
					
			}
		},
		init: function() {
			var self = this;
			self.thumbnails.init();
			self.sizeToggle.init();
		}
	},
	
	/*
	MODULE:mixed (core for the functions, but registration should be split into core, news, ent)
	*/
	apptap: {
		getEventPath: function(trigger) {
			if (path[-1] != "/") { path += "/"; } // Make sure there's a trailing /
			return path + trigger.attr("href"); // Adds the trigger's href anchor to the path
		},
		registerAppTapEvent: function(selector, eventName, isMacro, jsEventName, live) {
			var self = this,
				isMacro = isMacro || false,
				jsEventName = jsEventName || 'click',
				live = live || true;
				
			if (live) {
				// This event is safe to handle using jQuery's live function
				$(selector).live(jsEventName, function() {
					self.execAppTapEvent(this, eventName, isMacro);
				});
			} else {
				// This event is not safe to handle using jQuery's live function
				$(selector)[jsEventName](function() {
					self.execAppTapEvent(this, eventName, isMacro);
				});
			}
			
		},
		execAppTapEvent: function(trigger, eventName, isMacro) {
			var self = this,
				trigger = $(trigger),
				title = trigger.attr("title");
			sendEvent(trigger, eventName, title); // Tracks clicks for all events. Global sendEvent function comes from apptap itself
			if (isMacro) { retarget(self.getEventPath(trigger), title); } // Retargets apptap for macro events using global retarget function
		},
		init: function() {
			var self = this;
			self.registerAppTapEvent("#content_wrapper .macro_tab_header li a", "Event60", true, 'click', false); // Tabs that change majority of page
			self.registerAppTapEvent("#content_wrapper :not('#secondary_content') .tab_header:not('.macro_tab_header') li a", "Event61", false, 'click', false);    // Tabs that change part of the main content
			self.registerAppTapEvent("#secondary_content .tab_header li a", "Event62", false, 'click', false); // Tabs that change part of the sidebar
			self.registerAppTapEvent("#most_popular a", "Event22", false); // Clicks on most popular links 
			self.registerAppTapEvent("#page_wrapper a.comment_count", "Event23", false); // Clicks into comments from story titles
			self.registerAppTapEvent("#alert_bar_carousel .alert_bark_quicklinks a", "Event24", false); // Clicks on quicklinks bar
			self.registerAppTapEvent("#alert_bar_carousel .alert_bark:not(.alert_bark_quicklinks) a", "Event33", false); // Clicks on alert bar links
			/* Inline photo galleries */                                                                    
			self.registerAppTapEvent('#content_wrapper div.inline_photogallery .gallery_controls a', 'Event36', false); // Inline photo gallery scroll
			self.registerAppTapEvent('#content_wrapper ul.story_gallery li:not(.gallery_inline_prompt) a', 'Event37', false); // Inline photo gallery photo click
			self.registerAppTapEvent('#content_wrapper li.gallery_inline_prompt a', 'Event38', false); // Inline photo gallery full prompt click
					}
	},
	
	/*
	MODULE:core
	*/
	ratings: { // COMPLETE
		ratingSubmit: function(value, link) {
			var self = this,
				value = value || 0;
				
			if (value != 0) {
				// User has rated this
				var f = $(this.form);

				if(!f.hasClass("user_rating_form")) {
					f.addClass("user_rating_form");
				}
			} else {
				// User has cleared his rating
				var f = cancelThisForm;
				f.removeClass("user_rating_form");
			}
			$.post(f.attr("action"), {rating: value}, function(data, textStatus) {
				if (value == 0) {
					// Since user has cleared the rating, remove the users rating info and reinitialize this form
					// To get the overall rating
						newData = eval("(" + data + ")"),
						newRating = newData.rating,
						inputs = f.children('input[type=radio].rating-star'),
						input = inputs.filter("[value=" + newRating + "]"),
						overallInput = $(input[0]);

					overallInput.attr("checked", "checked");
					inputs.attr("class", "rating-star");

					self.init(inputs, f);
				}
			});
		},
		init: function(inputObjects, cancelScope) {
			var self = this;
			
			inputObjects.rating( {callback: self.ratingSubmit} );
			$('.star-rating-readonly').click(function() {
				alert("Please login to rate content");
			});
			cancelScope.find('div.rating-cancel a').click(function() {
				cancelThisForm = $($(this).parents("form")[0]);
			});
		}
	},
	
	/*
	MODULE:news
	*/
	cellar: {
		/**
		 * Enables showing/hiding of link categories in the cellar
		 * @method doCellarLinks
		 * @param {String} dlSelector The selector jQuery will use to get the definition list
		 * @param {String} promptText The text used to tell the user more links can be revealed
		 * @param {String} showText The text used when toggling links to show
		 * @param {String} hideText The text used when toggling links to hide
		 * Modified by Ryan Berg October 21, 2009
		 */
		doCellarLinks: function(dlSelector, promptText, showText, hideText) {
			var dl = $(dlSelector), // The definition list
				dtSelector = dlSelector + '_prompt', // Selector for the dt prompt
				ddSelector = dlSelector + '_links'; // Selector for the dd links

			if (dl.length) {
				var dt = $(dtSelector),
					dd = $(ddSelector);

				dd.hide();

				dt.append('<a href="' + dlSelector + '">' + promptText + '</a>'); // Add a link for user show/hide the links

				var dtToggle = $(dtSelector + ' a'); // Get the toggle link that was just added

				dtToggle.click(function(clickEvent) {
					// On each click, prevent the default anchor action, then toggle the bdLinks display
					clickEvent.preventDefault();
					var a = $(this);
					var t = a.text(); // The text of the toggle link

					dd.toggle(); // Show/hide the links
					// Swap the link verbage
					if (t.search(showText) >=0) {
						a.text(t.replace(' ' + showText, ' ' + hideText));
					} else {
						a.text(t.replace(' ' + hideText, ' ' + showText));
					} 
				});
			}
		},
		init: function() {
			var self = this;
			self.doCellarLinks('#business_directory', ' See directory links', 'See', 'Hide');
			self.doCellarLinks('#jobs_by_city', '(click to expand)', 'expand', 'hide');
			self.doCellarLinks('#jobs_by_cat', '(click to expand)', 'expand', 'hide');
			self.doCellarLinks('#jobs_by_popular', '(click to expand)', 'expand', 'hide');
		}
	},
	
	/*
	MODULE:promotions
	*/
	promotions: {
		/**
		 * Enables showing/hiding of promotions bucket
		 * @method doPromotionsBucket
		 * @param {String} showText The text used when toggling link to show
		 * @param {String} hideText The text used when toggling link to hide
		 * Modified by Grainger Marlar August 31, 2010
		 */
	/*	doPromotionsBucket: function() {
			var showText='Show';
			var hideText='Hide';

			$('#promote_wrapper').prev().append('<a href="#" id="promote_prompt">'+hideText+'</a>');

			$('#promote_wrapper').show();

			$('a#promote_prompt').click(function(evt) {
				evt.preventDefault();
				if ($(this).html()==showText) {
					$(this).html(hideText);
				}
				else {
					$(this).html(showText);
				}
				$(this).parent().next('#promote_wrapper').toggle();
			});
		},
		init:function() {
			var self = this;
			self.doPromotionsBucket();
		} */
	},
	
	/*
	MODULE:core
	*/
	mobile: {
		showMobilePrompt: function() {
			var width = jSINGmobile.getBrowserWidth(); // This function comes from the mobile-redirect.js file
			if ($.cookie("preventMobileRedirect") && !$.cookie("hideMobilePrompt") && width != 0 && width <= 800) {
				var prompt = '<div id="mobile_prompt" style="width: 994px; height: 60px; margin: 0 auto 10px;line-height: 60px; font-size: 24px;"><a id="visitMobileSite" href="' + jSINGmobile.getMobileURL() + '" style="margin-left: 15px;">Visit our mobile-optimized site</a><a id="hideMobilePrompt" href="#" style="font-size: 18px; color: #444; margin-left: 15px; text-transform: uppercase">Hide this message</a></div>';
				$("#utilities_wrapper").prepend(prompt); // Write the prompt into the utilities header
				// Capture clicks on the hide link to set a cookie to never show this again
				$("#hideMobilePrompt").click(function(e) {
					e.preventDefault(); // Keep the link from clicking through
					$("#mobile_prompt").remove(); // Remove the div
					$.cookie("hideMobilePrompt", 1, { expires: 365, path: '/' }); // Set cookie so that it does not show anymore
				});

				// Capture clicks on the visit link to remove the prevent redirect cookie
				$("#visitMobileSite").click(function() {
					$.cookie("preventMobileRedirect", null, { path: '/' });
				});
			}
		},
		init: function() {
			this.showMobilePrompt();
		}
	},
	
	/*
	MODULE:news (or maybe split into its own file that gets compressed into our plugins js)
	*/
	popmenus: {
		doPlugin: function() {
			$.fn.popmenu = function(options) {
				var defaults = {
					menu: ".popmenu",
					activeClass: false,
					time: 1500,
					speed: "",
					effect: ""
				};
				var options = $.extend(defaults, options);
				var active_menu = null;
				var timeout;

				return this.each(function() {

					var button = $(this);
					var menu = $(this).next();

					button.click( function(e) {
						clearTimeout(timeout);

						// If the target menu is hidden, close all open menus and open target menu
						if (menu.is(':hidden')) {
							// Close all open menus and remove active state on related buttons
							$("" + options.menu + ":visible").each( function() {
								$(this).hide(options.speed);
								$(this).prev().removeClass(options.activeClass);
							});

							menu.show(options.speed);
							if (options.activeClass != false) {
								button.addClass(options.activeClass);
							}
						}
						// If the target menu is already visible, hide it
						else {
							menu.hide(options.speed);
							if (options.activeClass != false) {
								button.removeClass(options.activeClass);
							}
						}
						active_menu = menu.index(menu);

						return false;
					});

					// Clear timeout if user mouses back over the button
					button.mouseover( function() {
					   clearTimeout(timeout);
					   active_menu = menu.index(menu);
					});

					// Clear timeout if user mouses back over the menu
					menu.mouseover( function() {
						clearTimeout(timeout);
						active_menu = menu.index(menu);
					});

					// When mouse leaves the button, start a timer to close the menu
					button.mouseout( function() {
						timeout = setTimeout(
						function() {
							if(options.activeClass != false){
								button.removeClass(options.activeClass);
							}
							menu.hide(options.speed);
						}
						,options.time);

					  active_menu = null;
					});

					// When mouse leaves the menu, start a timer to close the menu
					menu.mouseout( function () {
						timeout = setTimeout(
						function() {
							if(options.activeClass != false) {
								button.removeClass(options.activeClass);
							}
							menu.hide(options.speed);
						}
						,options.time); 

						active_menu = null;
					});

					// Handle clicks that are not on a button or menu
					$(document).click(function(e) {
						if (active_menu == null) {
							$(options.menu + ":visible").each( function() {
								$(this).hide(options.effect, options.speed);
								$(this).prev().removeClass(options.activeClass);
							});
						}
					});

				});
			};
		},
		init: function() {
			var self = this,
				popMenus = $('.popmenu_button');
			
			if (popMenus.length) {
				self.doPlugin(); // Set up the jQuery plugin before using it
				popMenus.popmenu({
					activeClass: "active"
				});
			}
		}
	},
	
	/*
	MODULE:core
	*/
	videoPlayer: {
		supports_html5_video: function() {
			// function to determine the browser's compatibility with HTML5 video and codecs
			// returns: true or false based on the browser's compatibility with HTML5 video
			/*  OLD Algorithm
			var support = !!document.createElement('video').canPlayType;
			if (!support) { return false; }
			var v = document.createElement('video');
			return (v.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"') == "probably");
			*/
			if( navigator.userAgent.search(/(ipad)|(iphone)/gi) != -1 ) { return true; }
			return false;
		},
		doEmbedSWF: function(p) {
			var self = this;
			swfobject.embedSWF(p.embedParams.playerSrc, p.embedParams.containerId, p.embedParams.playerWidth,
				p.embedParams.playerHeight, "9.0.115", p.installSWFURL, p.flashvars, p.params, p.attrs, self.embedCallback);
				window[p.playerName] = document.getElementById(p.playerName);
		},
		doEmbedHTML5: function(p) {
			var self = this;
			if ( !self.playerContainer ) { self.playerContainer = $("#" + p.embedParams.containerId); }
			var videoTag = $('<div id="html5_' + p.embedParams.containerId + '" class="html5player">' +
					'<video controls="controls" width="' + p.embedParams.playerWidth + '" height="' + p.embedParams.playerHeight +
					'" poster="' + p.embedParams.videoThumb + '"><source src="' + p.embedParams.videoURL + '" type="video/mp4">' +
					'</video></div>');
			self.playerContainer.replaceWith(videoTag);
			self.playerContainer = videoTag;
			var hFive = html5Player.init("html5_" + p.embedParams.containerId, true);
		},
		doEmbedVideo: function(p) {
			var self = this;
			// perform the logic to figure out if we need to embed SWF or HTML5 versions of a video player
			if (self.supports_html5_video()) { self.doEmbedHTML5(p); }
			else { self.doEmbedSWF(p); }
		},
		embedPlayers: function() {
		/*
		jSINGconf.videoPlayers hash is set in base.html
		Each instance of player/player_include.html adds a player instance to the hash
		*/
			var self = this;
			for (var p in jSINGconf.videoPlayers) {
				self.doEmbedVideo(jSINGconf.videoPlayers[p]);
			}
		},
		changePlaylistPage: function(slug, pg) {
			var self = this;
			var itms = $("#"+slug).children("li");
			var totalPages = itms.length

			if(pg < 0) pg = 0;
			if(pg > (totalPages - 1)) pg = totalPages - 1;

			var curpage = jSINGconf.playlistPlayers[slug]['playlistCurrentPage'];

			var obj1 = $(itms[curpage]);
			var obj2 = $(itms[pg]);

			if (pg - curpage < 0) {
				$(itms).animate({left: '+=320'}, 500, function() { /**/ });
			}
			else if( pg - curpage > 0) {
				$(itms).animate({left: '-=320'}, 500, function() { /**/ });
			}

			jSINGconf.playlistPlayers[slug]['playlistCurrentPage'] = pg;
			self.updatePlaylistPosition(slug);

			return false;
		},
		updatePlaylistPosition: function(slug) {
			var self = this;
			var pparent = $('#'+slug).parent().parent();
			var indicator = $('.video_playlist_navigation', pparent).children('.position');
			var items = $('#'+slug).children('li').children('a').length;

			if (!jSINGconf.playlistPlayers[slug]['playlistCurrentPage']) { // assume page 0 if no page set
				jSINGconf.playlistPlayers[slug]['playlistCurrentPage'] = 0;
			}
			if (!self.playlistPerPage) { self.playlistPerPage = 4; }

			var s = (jSINGconf.playlistPlayers[slug]['playlistCurrentPage'] * self.playlistPerPage) + 1;
			var e = s + (self.playlistPerPage - 1);

			if (e > items) { e = items; }
				indicator.html("Showing "+ s +" - "+ e + " of "+ items +" videos");
		},
		embedCallback: function(e) {
			if (!e.success) {
				// Video was not embedded properly, show the fallback flash download HTML instead of the loader
				var playerContainer = $("#" + e.id);
				playerContainer.html(playerContainer.data('fallbackHTML'));
			}
		},
		showLoaders: function() {
			$('.player_container').each(function() {
				var playerContainer = $(this);

				// Store the flash download fallback HTML
				playerContainer.data('fallbackHTML', playerContainer.html());

				// Replace the flash download HTML with a loading gif
				// TODO: Ditch the embedded style
				playerContainer.html('<img src="' + jSINGconf.theme.CORP_MEDIA_URL + '/img/ajax-loader-video.gif" alt="Loading video" style="margin-top: 20px; text-align: center;"/>');
	 		});
		},
		init: function() {
			/*
			DO NOT CALL THIS INIT FROM jSING.init()
			because the required SWFObject library isn't loaded until *after* jSING.init() runs
			*/
			var self = this;
			this.showLoaders();

			if( $('#homepage_video_poster').length ){ self.initVideoPoster(); }
			if( $('.video_playlist').length ){ self.initVideoPlaylist(); }
		},
		initVideoPlaylist: function() {
			var self = this;

			$(".video_playlist").each(function(){
				var itms = $(this).children("li");
				//var baseWidth = Number($(this).css("width").replace("px", ""));
				var baseWidth = 320;
				var initial = Number(itms.first().css("left").replace("px", ""));
				var pslug = $(this).attr('id');

				jSINGconf.playlistPlayers[pslug]['playlistCurrentPage'] = 0;
				self.updatePlaylistPosition(pslug);

				itms.each(function(index){
					$(this).css("left", initial + (baseWidth * index));
				});

				var pparent = $(this).parent().parent();

				$('.playlist_link', pparent).click(function() {
					var bits = $(this).attr('href').split('/');
					var slug = bits[bits.length-1];
					self.doEmbedVideo(jSINGconf.playlistPlayers[pslug]['videos'][slug]);
					return false;
				});

				$('.video_playlist_navigation', pparent).children('a').children('.prev').click(function(){
					return self.changePlaylistPage(pslug, jSINGconf.playlistPlayers[pslug]['playlistCurrentPage']-1);
				});
				$('.video_playlist_navigation', pparent).children('a').children('.next').click(function(){
					return self.changePlaylistPage(pslug, jSINGconf.playlistPlayers[pslug]['playlistCurrentPage']+1);
				});

				$(this).css('width', (itms.length-1) * baseWidth);
			});

			//$('.video_playlist_wrapper').css('overflow', "hidden;");
		},
		initVideoPoster: function() {
			var self = this;
			$("#homepage_video_poster").click(function() {
				self.doEmbedVideo(jSINGconf.videoPoster);
				return false;
			});
		}
	},
	
	/*
	MODULE:core
	*/
	staffmembers: {
		dropdownSelect: function() {
			//Remove the search button since we are not really searching...
			$("input#search_button").remove();
			
			//use an id of #dropdown_select on your form
			//wrap all the results divs in #dropdown_select_wrapper			
			$("#dropdown_select").change(function(){
				$("#" + this.value).show().siblings().hide();
			});
			
			$("#dropdown_select").change();
			
		},
		
		init: function() {
			var self = this;
			if($("#dropdown_browse").length) {
				self.dropdownSelect();
			}
		}
		
	},
	
	/*
	MODULE:core
	*/
	cinemasource: {
		movieSelect: function() {
			$(".movieSelect").change(function(){
	
				var field = $(this),
					fieldVal = field.val();
				
				movieUrl = "/news/entertainment/movies/" + fieldVal + '/0/';

				window.location.href = window.location.protocol + '//' + window.location.hostname + movieUrl;
			});
		
			$(".theaterSelect").change(function(){
	
				var field = $(this),
					fieldVal = field.val();
				
				theaterUrl = "/news/entertainment/movies/theaters/" + fieldVal + '/0/';

				window.location.href = window.location.protocol + '//' + window.location.hostname + theaterUrl;
			});
		},
		
		init: function() {
			var self = this;
			if($(".movieSelect").length) {
				self.movieSelect();
			}
		}
		
	},
	
	/*
	MODULE:core
	*/
	browsers: {
		zIndexIE: function() {
			if ($.browser.msie) {
				var zIndexNumber = 10000,
					zList = $("div"),
					zListLength = zList.length;

				for (var i = 0; i < zListLength; i++) {
					zList[i].style.zIndex = zIndexNumber;
					zIndexNumber -= 10;
				}
			}
		},
		init: function() {
			this.zIndexIE();
		}
	},

// new alert bar
	alertBarkCarousel: {
		doAlertBarCarousel: function(carousel) {
			var controls = '<div id="alert_bark_controls"><a id="alert_bark_prev" class="alert_bark_control alert_bark_prev" href="#alert_bark_carousel">Previous</a><div id="alert_bark_counter"><span id="alert_bark_counter_current">1</span> of <span id="alert_bark_counter_total">' + carousel.size() + '</span></div><a id="alert_bark_next" class="alert_bark_control alert_bark_next" href="#alert_bark_carousel">Next</a></div>';

			$('#alert_bark_clipper').after(controls);

			// Disable autoscrolling if the user clicks the prev or next button.
			$('#alert_bark_prev').bind('click', function() {
				carousel.prev();
				carousel.startAuto(0);
				return false;
			});

			$('#alert_bark_next').bind('click', function() {
				carousel.next();
				carousel.startAuto(0);
				return false;
			});

			jSING.carousels.utils.pauseAutoScrollOnHover(carousel);

		},

		alertBarOnSlide: function(carousel, item, current_page, state) {
			$("#alert_bark_counter_current").text(current_page);
		},

		init: function() {
			var alertBar = $("#alert_bark_carousel");
			if (alertBar.children().size() > 1) {
				alertBar.jcarousel({
					vertical: false,
					auto: 6,
					animation: 250,
					easing: 'swing',
					scroll: 1,
					wrap: 'both',
					initCallback: this.doAlertBarCarousel,
					itemFirstInCallback: this.alertBarOnSlide,
					buttonNextHTML: null,
					buttonPrevHTML: null
				});
				$('#alert_bark_wrapper').hover(function() {
					$("#alert_bark_controls").stop(false, true).fadeTo(100, 1);
				}, function() {
					$("#alert_bark_controls").stop(false, true).hide(150, 0);
				});
			}
		}
	},

//end new alert bar	
	
	/*
	MODULE:mixed
	*/
	init: function(pageType) {
		/* Call on ready to initialize the needed functions */
		var self = this; // Cache this to reduce lookups
		self.navigation.init();
		self.tabs.init();
		self.megaDropdownSelect.init();
		self.entMegaNav.init();
		self.checkBoxSelect.init();
		self.lightBox.init();
		self.ratings.init($('input[type=radio].rating-star'), $('form.rating_form'));
		self.carousels.init();
		self.login.init();
		self.comments.init();
		self.photos.init();
		self.videoPlayer.init();
		self.popmenus.init();
		self.calendars.init();
		self.maps.init();
		self.mobile.init();
		self.staffmembers.init();
		self.cinemasource.init();
		self.cellar.init();
		self.apptap.init();
		self.browsers.init();
		self.alertBarkCarousel.init();
		self.neighborhoodMaps.init();
//		self.promotions.init();

		
	}
	
}
//var startTimeInit = startTimeAlert();
// At end of the page
jSING.init();

