﻿// Re-Used CSS Classes and Document Object IDs
var selectedButtonClass = 'storedLocatorSelectedButton';
var deselectedButtonClass = 'storedLocatorSelectedButton deselected';
var loaderClass = 'storeLocatorLoader';
var loaderStatusClass = 'storeLocatorLoaderStatus';
var linkContainerClass = 'storeLocatorLinkContainer';
var storeLocatorGoButtonClass = 'storeLocatorGoButton';
var storeLocatorBubbleClass = 'storeLocatorBubbleLocation ';
var storeLocatorWebsiteClass = 'storeLocatorLocationWebsite';
var storeLocatorDirectionsLinkClass = 'storeLocatorDirectionsLink';
var storeLocatorMaxClass = 'storeLocatorBubbleLocationMax';
var storeLocatorMaxMapClass = 'storeLocatorMaximizedMap';
var storeLocatorBubbleAddressClass = 'storeLocatorBubbleAddress ';
var storeLocatorMaximizedProductsClass = 'storeLocatorMaximizedProducts';
var storeLocatorMaximizedEventsClass = 'storeLocatorMaximizedEvents';
var storeLocatorBubbleTitleClass = 'storeLocatorBubbleTitle';
var storeLocatorResultLocationsClass = 'storeLocatorResultLocations';
var storeLocatorResultLocationsStatusClass = 'storeLocatorResultLocationsStatus';
var storeLocatorStatusTextClass = 'storeLocatorStatusText';
var storeLocatorLocationClass = 'storeLocatorLocation ';
var storeLocatorPagingControlsClass = 'storeLocatorPagingControls';
var storeLocatorPreviousClass = 'storeLocatorPrevious';
var storeLocatorNextClass = 'storeLocatorNext';
var storeLocatorPagingResultsTextID = 'storeLocatorPagingResultsText';
var eventButtonID = 'eventButton';
var addressTextBoxID = 'addressTextBox';
var getMapButtonID = 'getMap';
var buyOptionsID = 'buyOptions';
var playOptionsID = 'playOptions';
var resultsTableID = 'storeLocatorResultsTable';
var storeButtonID = 'storeButton';
var storeLocatorDirectionsFormID = 'storeLocatorDirectionsForm';
var storeLocatorSearchFormID = 'storeLocatorSearchForm';
var storeLocatorDirectionsFormLinksID = 'storeLocatorDirectionsFormLinks';
var storeLocatorAddressNotLocated = 'storeLocatorAddressNotLocated';
var storeLocatorDateControlsID = 'storeLocatorDateControlsContainer';
var showBuyID = 'showBuy';
var showPlayID = 'showPlay';
var startDateID = 'startDate';
var endDateID = 'endDate';
var startDateParentID = 'startDateCalendarParent';
var endDateParentID = 'endDateCalendarParent';
var dateControlsInnerContainer = 'storeLocatorDateControlsInnerContainer';

// Temporary Messages Until Resources Can Be Added
//var troubleProcessingMessage = 'There was a problem while trying to process the request. It is likely that there is a network-related outage, or the service is undergoing maintenance.';
//var Resources.get('EventStartDateMessage') = 'Please choose an event start date that is before the event end date.';
//var endDateMessage = 'Please choose an event end date that is after the event start date.';

// Settings
var resultsPerPage = 12;
var currentMode = 'buy';

// Application State
var icons = new Hash();
var visibleMarkers = new Hash();
var map = null;
var geocoder = null;
var currentLocations = null;
var progressBar = null;
var tableProgressBar = null;
var latitude = null;
var longitude = null;
var lastBounds = null;
var currentDirections = null;
var validSearchCondition = false;
var linkContainer = null;
var messageContainer = null;
var lastInfoWindowID = null;
var locationIDToOpen = null;
var lastDirectionsQuery = null;
var startDateCal = null;
var endDateCal = null;

// Load Google Maps
google.load('maps', '2');
google.setOnLoadCallback(SetupGoogleMaps);

// Initial Event Registration
Event.observe(window, 'load', SetupEvents);
Event.observe(window, 'unload', BodyUnload);

// Functions

/*  
    This function registers for events on the different document objects we need
    to listen to. In this case, the mode buttons as well as the key press on the 
    address text box.
*/
function SetupEvents()
{
    SetupButtons();
    
    Event.observe($(addressTextBoxID), 'keypress', GetMapEnter);
    
    startDateCal = Calendar.setup({
      dateField     : startDateID,
      parentElement : startDateParentID,
      selectHandler : StartDateSelected
    });
    
    endDateCal = Calendar.setup({
      dateField     : endDateID,
      parentElement : endDateParentID,
      selectHandler : EndDateSelected
    });
                
    var date = new Date();
    date.setTime(CurrentTime);
            
    var date2 = new Date();
    date2.setTime(CurrentTime);
            
    startDateCal.update(date);  
    endDateCal.update(date2);
}

function DisplayDateControls(event, control)
{
    $($(control).parentNode).remove();
    
    $(dateControlsInnerContainer).style.display = 'block';
}

/*
    This function will set the day for the start date if it was selected from the calendar.
*/
function StartDateSelected(calendar, dateSelected, selectDay)
{
    if(selectDay)
    {
        if(endDateCal.dateField.value == '' || endDateCal.date >= calendar.date)
        {
            calendar.dateField.value = dateSelected;
            
            $(startDateParentID).style.display = 'none';
        }
        else
        {
            alert(Resources.get('EventStartDateMessage'));
        }
    }
}

/*
    This function will set the day for the end date if it was selected from the calendar.
*/
function EndDateSelected(calendar, dateSelected, selectDay)
{
    if(selectDay)
    {
        if(startDateCal.dateField.value == '' || startDateCal.date <= calendar.date)
        {
            calendar.dateField.value = dateSelected;
            
            $(endDateParentID).style.display = 'none';
        }
        else
        {
            alert(Resources.get('EventEndDateMessage'));
        }
    }
}

/*
    This function will display the calendar.
*/
function DisplayCalendar(parentID, otherID)
{
    var parentDiv = $(parentID);
    var otherDiv = $(otherID);
    
    if(parentDiv != null && otherDiv != null)
    {
        if(parentDiv.style.display == 'none')
        {            
            parentDiv.style.display = 'block';
            
            if(otherDiv.style.display != 'none')
            {
                otherDiv.style.display = 'none';
            }
        }
        else
        {
            parentDiv.style.display = 'none';
        }
    }
}

/*
    This function sets up the google maps default state as well as contains the
    class definitions for the link container and the progress bar that appears
    on the map and in the table area.
*/
function SetupGoogleMaps()
{
    /*
        This function is actually a class that represents the link container that shows up
        when you click on the "Link" button.
    */
    function LinkContainer() 
    {
        this.container = null;
        this.input = null;
    }
    
    /*
        This function fades the link container out so when you close it a nice fading
        action is displayed.
    */
    function FadeLinkContainer(linkContainer, alpha)
    {
        if(alpha > 0 && (linkContainer.style.opacity == '' || linkContainer.style.opacity == null || (alpha / 100) <= linkContainer.style.opacity))
        {
            var a = alpha - 30;
            
            linkContainer.style.filter = 'alpha(opacity=' + a + ')';
            linkContainer.style.MozOpacity = (a / 100) + ';';
            linkContainer.style.opacity = (a / 100);
            
            setTimeout(function() { FadeLinkContainer(linkContainer, a); }, 75);
        }
        else if(alpha <= 0)
        {            
            linkContainer.style.display = 'none';
            
            linkContainer.style.filter = 'alpha(opacity=100)';
            linkContainer.style.MozOpacity = '100;';
            linkContainer.style.opacity = 100;
        }
        else
        {
            linkContainer.style.filter = 'alpha(opacity=100)';
            linkContainer.style.MozOpacity = '100;';
            linkContainer.style.opacity = 100;
        }
    }
    
    /*
        This function defines this class as a Google control that
        can be added to a map.
    */
    LinkContainer.prototype = new GControl();
    
    /*
        This function is required for adding to a Google map. Google invokes
        the initialize function when it's ready to add the control to the map
        and then calls this method so it can be appended to the map.
    */
    LinkContainer.prototype.initialize = function(map) 
    {
        this.create();

        map.getContainer().appendChild(this.container);        

        return this.container;
    }
    
    /*
        This function creates the necessary document elements that makeup the
        link container control. Namely a parent, link for closing it, as well as
        the input text box which contains the actual link.
    */
    LinkContainer.prototype.create = function()
    {
        var linkContainer = new Element('div', { 'class': linkContainerClass }).addClassName(linkContainerClass);
        var closeLink = new Element('a', { 'href': 'javascript:void(0);' }).update('&nbsp;');
        var linkInput = new Element('input', { 'type': 'text', 'readonly': 'readonly' });
        
        Event.observe(closeLink, 'click', this.close.bindAsEventListener(this, linkContainer));
        
        linkContainer.insert(closeLink);
        linkContainer.insert(new Element('span').update(Resources.get('LinkToPaste') + ':'));
        linkContainer.insert(linkInput);
        
        this.container = linkContainer;  
        this.input = linkInput;
        
        return linkContainer;    
    }

    /*
        This function tells the Google map where to position the control. In this case, the upper
        right corner of the map, with a 0 pixel offset.
    */
    LinkContainer.prototype.getDefaultPosition = function() 
    {
        return new GControlPosition(G_ANCHOR_TOP_RIGHT, new google.maps.Size(0, 0));
    }
    
    /*
        This function tells the Google map that the control should be selectable. In other words, we
        want the user to be able to select text from the input box.
    */
    LinkContainer.prototype.selectable = function() 
    {
        return true;
    }
    
    /*
        This function is invoked when the link container is closed with the little close
        x-mark.
    */
    LinkContainer.prototype.close = function(event, linkContainer)
    {
        FadeLinkContainer(linkContainer, 100);
    }
    
    /*
        This function displays the link container by just setting its display property
        to "block".
    */
    LinkContainer.prototype.show = function()
    {
        this.container.style.display = 'block';
    }
    
    /*
        This function sets the value of the text box that is displayed in the link
        container.
    */
    LinkContainer.prototype.setLink = function(link)
    {
        this.input.value = link;
        this.input.focus();
        this.input.select();
    }
    
    /*
        This function is actually a class that represents the message container that shows up
        when a message needs to be displayed.
    */
    function MessageContainer() 
    {
        this.container = null;
        this.messageSpan = null;
    }
    
    /*
        This function fades the message container out so when you close it a nice fading
        action is displayed.
    */
    function FadeMessageContainer(messageContainer, alpha)
    {
        if(alpha > 0 && (messageContainer.style.opacity == '' || messageContainer.style.opacity == null || (alpha / 100) <= messageContainer.style.opacity))
        {
            var a = alpha - 30;
            
            messageContainer.style.filter = 'alpha(opacity=' + a + ')';
            messageContainer.style.MozOpacity = (a / 100) + ';';
            messageContainer.style.opacity = (a / 100);
            
            setTimeout(function() { FadeMessageContainer(messageContainer, a); }, 75);
        }
        else if(alpha <= 0)
        {            
            messageContainer.style.display = 'none';
            
            messageContainer.style.filter = 'alpha(opacity=100)';
            messageContainer.style.MozOpacity = '100;';
            messageContainer.style.opacity = 100;
        }
        else
        {
            messageContainer.style.filter = 'alpha(opacity=100)';
            messageContainer.style.MozOpacity = '100;';
            messageContainer.style.opacity = 100;
        }
    }
    
    /*
        This function defines this class as a Google control that
        can be added to a map.
    */
    MessageContainer.prototype = new GControl();
    
    /*
        This function is required for adding to a Google map. Google invokes
        the initialize function when it's ready to add the control to the map
        and then calls this method so it can be appended to the map.
    */
    MessageContainer.prototype.initialize = function(map) 
    {
        this.create();

        map.getContainer().appendChild(this.container);        

        return this.container;
    }
    
    /*
        This function creates the necessary document elements that makeup the
        message container control. Namely a parent, link for closing it, as well as
        the input text box which contains the actual link.
    */
    MessageContainer.prototype.create = function()
    {
        var messageContainer = new Element('div', { 'class': linkContainerClass }).addClassName(linkContainerClass);
        var closeLink = new Element('a', { 'href': 'javascript:void(0);' }).update('&nbsp;');
        var messageSpan = new Element('span');
        
        Event.observe(closeLink, 'click', this.close.bindAsEventListener(this, messageContainer));
        
        messageContainer.insert(closeLink);
        messageContainer.insert(messageSpan);
        
        this.container = messageContainer;  
        this.messageSpan = messageSpan;
        
        return messageContainer;    
    }

    /*
        This function tells the Google map where to position the control. In this case, the upper
        right corner of the map, with a 0 pixel offset.
    */
    MessageContainer.prototype.getDefaultPosition = function() 
    {
        return new GControlPosition(G_ANCHOR_TOP_RIGHT, new google.maps.Size(0, 0));
    }
    
    /*
        This function tells the Google map that the control should be selectable. In other words, we
        want the user to be able to select text from the message.
    */
    MessageContainer.prototype.selectable = function() 
    {
        return true;
    }
    
    /*
        This function is invoked when the link container is closed with the little close
        x-mark.
    */
    MessageContainer.prototype.close = function(event, messageContainer)
    {
        FadeMessageContainer(messageContainer, 100);
    }
    
    /*
        This function displays the link container by just setting its display property
        to "block".
    */
    MessageContainer.prototype.show = function()
    {
        this.container.style.display = 'block';
    }
    
    /*
        This function sets the value of the text box that is displayed in the link
        container.
    */
    MessageContainer.prototype.setMessage = function(message)
    {
        this.messageSpan.update(message);
    }

    /*
        This function is actually a class for the loading progress bar that is displayed on the Google
        map while it's loading markers and on the table while it updates. It is a Google control
        that is displayed on the map as well as used by other parts of the application.
    */
    function LoadingMessage() 
    {
        this.bar = null;
        this.container = null;
        this.status = null;
        this.percent = null;
        this.events = new Array();
        this.percentage = null;
    }
    
    /*
        This function fades the loading message. When the progress bar is filled to a 100%, this function
        gets invoked and slowly fades the message away. When finished, all the fadeComplete events are all
        fired.
    */
    function FadeLoadingMessage(barEvents, loadingMessage, alpha)
    {
        if(alpha > 0 && (loadingMessage.style.opacity == '' || loadingMessage.style.opacity == null || (alpha / 100) <= loadingMessage.style.opacity))
        {
            var a = alpha - 10;
            
            loadingMessage.style.filter = 'alpha(opacity=' + a + ')';
            loadingMessage.style.MozOpacity = (a / 100) + ';';
            loadingMessage.style.opacity = (a / 100);
            
            setTimeout(function() { FadeLoadingMessage(barEvents, loadingMessage, a); }, 75);
        }
        else if(alpha <= 0)
        {            
            loadingMessage.style.display = 'none';
            
            loadingMessage.style.filter = 'alpha(opacity=100)';
            loadingMessage.style.MozOpacity = '100;';
            loadingMessage.style.opacity = 100;
            
            for(var i = 0; i < barEvents.length; i++)
            {
                var aEvent = barEvents[i];
                
                if(aEvent.type == 'fadeComplete')
                {
                    aEvent();
                }
            }
        }
        else
        {
            loadingMessage.style.filter = 'alpha(opacity=100)';
            loadingMessage.style.MozOpacity = '100;';
            loadingMessage.style.opacity = 100;
        }
    }
    
    /*
        This function defines this class as a Google control that
        can be added to a map.
    */
    LoadingMessage.prototype = new GControl();
    
    /*
        This function is invoked by the Google maps API to load the control to the map.
        The map object is passed in and it appends the necessary document elements
        to the map.
    */
    LoadingMessage.prototype.initialize = function(map) 
    {
        this.create();

        map.getContainer().appendChild(this.status);        

        return this.status;
    }
    
    /*
        This function creates the necessary document elements that make up the
        progress bar.
    */
    LoadingMessage.prototype.create = function()
    {
        var status = new Element('div', { 'class': loaderStatusClass }).addClassName(loaderStatusClass);
        var background = new Element('div', { 'class': loaderClass }).addClassName(loaderClass);
        var span = new Element('span');
        var p = new Element('p');
        
        background.update(span);
        status.insert(p);
        status.insert(background);
        
        this.container = background;  
        this.bar = span;
        this.status = status;
        this.percent = p;
        
        return status;    
    }

    /*
        This function tells the map where to place the progress bar. In this case, the upper
        right hand corner of the map with a 10 pixel offset.
    */
    LoadingMessage.prototype.getDefaultPosition = function() 
    {
        return new GControlPosition(G_ANCHOR_TOP_RIGHT, new google.maps.Size(10, 10));
    }
    
    /*
        This function sets the percent complete of the progress bar. It takes the percent
        in the form of a decimal number.
    */
    LoadingMessage.prototype.SetProgress = function(percent)
    {
        if(percent >= 1)
        {
            this.bar.style.width = '148px';
            this.percent.update('100%');
            
            var barContainer = this.status;
            var barEvents = this.events;
            
            setTimeout(function() { FadeLoadingMessage(barEvents, barContainer, 100); }, 10);            
        }
        else
        {           
            this.status.style.display = 'block';
            this.bar.style.width = percent * 148 + 'px';
            this.percent.update(Math.round(percent * 100) + '%');
        }
        
        this.percentage = percent;
    }
    
    /*
        This function returns the current progress of the progress bar.
    */
    LoadingMessage.prototype.GetProgress = function(percent)
    {
        return this.percentage;
    }
    
    /*
        This function allows you to register for the 'fadeComplete' event
        of the progress bar. It is fired when it has faded completely away and
        is no longer visible to the user.
    */
    LoadingMessage.prototype.Observe = function(eventType, newEvent)
    {
        newEvent.type = eventType;
        
        this.events.push(newEvent);
    }

    // Initialize all of the application objects we'll need, including the Client Geocoder
    // as well as the progress bars, link container, and pan control.
    geocoder = new google.maps.ClientGeocoder();
    progressBar = new LoadingMessage();
    tableProgressBar = new LoadingMessage();
    linkContainer = new LinkContainer();
    messageContainer = new MessageContainer();
    panControl = new GSmallMapControl();
    
    // Initialize the actual Google map and center it on the United States with a zoom level of
    // 3 which should fit the whole US in the map window. Add all of the controls to the map
    // when finished.
    map = new google.maps.Map2($('map'));
    map.setCenter(new google.maps.LatLng(Resources.get('DefaultLatitude'), Resources.get('DefaultLongitude')), parseInt(Resources.get('DefaultZoom')));
    map.addControl(panControl);
    map.addControl(progressBar);    
    map.addControl(linkContainer);
    map.addControl(messageContainer);
    
    map.DefaultLatitude = Resources.get('DefaultLatitude');
    map.DefaultLongitude = Resources.get('DefaultLongitude');
    map.DefaultZoom = parseInt(Resources.get('DefaultZoom'));
    
    // Subscribe to the map events we want to listen to so we can update the map with new
    // stores and venues at appropriate times.
    google.maps.Event.addListener(map, 'dragend', UpdateMap.bindAsEventListener(false));
    google.maps.Event.addListener(map, 'zoomend', UpdateMap.bindAsEventListener(false));
    
    // Setup the icons hash, which contains all of the marker icons used on the map.
    icons.set('Core', new google.maps.Icon(G_DEFAULT_ICON, '/StoreAndEventLocator/Images/red_pushpin_small.png'));
    icons.set('Premier', new google.maps.Icon(G_DEFAULT_ICON, '/StoreAndEventLocator/Images/blue_pushpin_small.png'));
    icons.set('Mass', new google.maps.Icon(G_DEFAULT_ICON, '/StoreAndEventLocator/Images/green_pushpin_small.png'));
    icons.set('Venue', new google.maps.Icon(G_DEFAULT_ICON, '/StoreAndEventLocator/Images/purple_pushpin_small.png'));
    
    icons.each(function(pair) {
        pair.value.iconSize = new google.maps.Size(15, 26);
        pair.value.shadow = '';
        pair.value.shadowSize = new google.maps.Size(16, 28);
        pair.value.iconAnchor = new google.maps.Point(7, 26);
    });
    
    // Call the SetupMapState function so we can restore the map to a previous state if map state was
    // provided by the page.
    SetupMapState();
}

/*
    This function will attempt to restore the state of the application, to the best of its abilities, 
    if map state was provided by the web page. It will only do this if the map state is not null. It does this
    by calling certain functions, setting state variables. Unfortunately, it's not easy to perfectly restore
    the map to the origional state. Perhaps this function should have been written first to better define
    the method signatures.
*/
function SetupMapState()
{
    if(mapState != null)
    {
        // First, make sure we have a latitude and longitude.
        if(mapState.MapLatitude != null && mapState.MapLongitude != null)
        {
            var zoom = 3;
         
            // If a zoom level was provided, use that, otherwise use the default of 3.   
            if(mapState.ZoomLevel != null)
            {
                zoom = mapState.ZoomLevel;
            }
            
            // Center the map to the coordinates provided and the zoom level determined.
            map.setCenter(new google.maps.LatLng(mapState.MapLatitude, mapState.MapLongitude), parseInt(zoom)); 
            	        
            // If the mode doesn't equal buy, change the mode. Buy is the default, so that's why we check
            // for that first.
	        if(mapState.Mode != 'buy')
	        {
	            ChangeMode(false);
	            
	            $(eventButtonID).className = selectedButtonClass;
	            
	            if(mapState.StartDate != null || mapState.EndDate != null)
	            {
	                DisplayDateControls(null, $('displayDateLink'));
	            }
	        }
	        
	        $(startDateID).value = mapState.StartDate;
	        $(endDateID).value = mapState.EndDate;
	        
	        // Check all of the products, events, and event brands that were checked last time. These functions
	        // will individually check for null arrays.
	        SetCheckedBoxes('product', mapState.CheckedProducts);
	        SetCheckedBoxes('event', mapState.CheckedEvents);
	        SetCheckedBoxes('eventbrand', mapState.CheckedEventBrands);
	        
	        // Set the location ID to open if one was given. The functions that go through and open the markers
	        // check this value and if a match is made, it will open the current marker it's on.
	        locationIDToOpen = mapState.OpenMarkerID;
	        
	        // Set the valid search condition if an address was provided. This is necessary, otherwise
	        // a search won't be made.
	        if(mapState.Search != null && mapState.Search != '')
	        {
	            $(addressTextBoxID).value = mapState.Search;
	            
	            validSearchCondition = true;
	        }
	        
	        // We've set all the state we need to, tell the map to update (which should also tell the table
	        // to update as well).
	        UpdateMap(true); 
	        
	        // We need to do some cleanup things after the state has been restored...so we'll subscribe
	        // to the progress bar fadeComplete event so we can do those things.
	        tableProgressBar.Observe('fadeComplete', function() {
	            if(mapState.SelectedTablePage != null)
	            {
	                // If an old page was provided (the user paged to another page), lets set that
	                // and restore the current page on the table.
	                var oldPage = parseInt(mapState.SelectedTablePage) - 1;
	                
	                if(oldPage > 0)
                    {
                        UpdateLocationsTableLocations(oldPage);                   
                        UpdateLocationsTablePagingControls(currentLocations.length, oldPage);
                    }
    	            
	                mapState.SelectedTablePage = null;
	            }
	            
	            // If directions were used last time, lets restore them as well.
	            if(mapState.LastDirectionsQuery != null && mapState.LastDirectionsQuery != '' && mapState.LastDirectionsQuery != 'null')
	            {
	                lastDirectionsQuery = mapState.LastDirectionsQuery;
	                
	                LoadDirections(mapState.LastDirectionsQuery);
	            }
	        });
        }
        else
        {
            if(mapState.Search != null && mapState.Search != '')
            {
                $(addressTextBoxID).value = mapState.Search;
                
                // If the mode doesn't equal buy, change the mode. Buy is the default, so that's why we check
                // for that first.
	            if(mapState.Mode != 'buy')
	            {
	                ChangeMode(false);
    	            
	                $(eventButtonID).className = selectedButtonClass;
	            }
    	        
	            // Check all of the products, events, and event brands that were checked last time. These functions
	            // will individually check for null arrays.
	            SetCheckedBoxes('product', mapState.CheckedProducts);
	            SetCheckedBoxes('event', mapState.CheckedEvents);
	            SetCheckedBoxes('eventbrand', mapState.CheckedEventBrands);
                
                GetMap(null);	            
            }
        }
    }
}

/*
    This function will display the link container as well as populate it with a
    link that the page determines.
*/
function DisplayLinkContainer()
{        
    GetStateLink('Default', function(transport) {
        linkContainer.show();   
        linkContainer.setLink(transport.responseText);
    });
}

/*
    This function will return a state link which has all of the necessary values in it
    to repopulate the map. This can then be given to the user via the link container
    on the map.
*/
function GetStateLink(mode, delegate)
{
    var result = null;
    var markerID = null;
    var infoWindow = map.getInfoWindow();
    
    if(infoWindow != null && !infoWindow.isHidden())
    {
        markerID = lastInfoWindowID;
    }
    
    new Ajax.Request(window.location.pathname, {
            method: 'get',
            parameters: { GetLink: 'GetLink',
                          Mode: mode,
                          ReturnParamMapCenter: map.getCenter().toString(), 
                          ReturnParamMapZoom: map.getZoom(),
                          ReturnParamMapSearch: $(addressTextBoxID).value,
                          ReturnParamMode: currentMode,
                          ReturnParamCheckedProducts: escape(GetCheckBoxes('product', true, ';')),
                          ReturnParamCheckedEvents: escape(GetCheckBoxes('event', true, ';')),
                          ReturnParamCheckedEventBrands: escape(GetCheckBoxes('eventbrand', true, ';')),
                          ReturnParamOpenMarkerID: markerID,
                          ReturnParamTablePage: GetSelectedTablePage(),
                          ReturnParamLastDirectionsQuery: escape(lastDirectionsQuery),
                          ReturnParamStartDate: escape($(startDateID).value),
                          ReturnParamEndDate: escape($(endDateID).value)},
            onSuccess:  delegate
        });
}

/*
    This function will setup the buttons that are used for the buy and play options
    as well as the get map button.
*/
function SetupButtons()
{
    var buttons = $$('.' + selectedButtonClass, '.' + deselectedButtonClass);
    var submit = $(getMapButtonID);
    
    for(var i = 0; i < buttons.length; i++)
    {
        var button = buttons[i];
        
        Event.observe(button, 'mouseover', SetupButton);
        Event.observe(button, 'mouseout', SetupButton);
        Event.observe(button, 'click', ToggleButton);
    }
    
    Event.observe(submit, 'mouseover', SetupButton);
    Event.observe(submit, 'mouseout', SetupButton);
    Event.observe(submit, 'click', GetMap);
}

/*
    This function will switch the button state from hover to selected. This is done by
    changing their background position.
*/
function SetupButton(event)
{
    var button = Event.element(event);
    
    if(button != null)
    {
        if((button.style.backgroundPosition == '' || button.style.backgroundPosition == '0% 0%') && button.className != selectedButtonClass)
        {
            if(button.className == deselectedButtonClass)
            {
                button.style.backgroundPosition = '-157px 0px';
            }
            else
            {
                button.style.backgroundPosition = '-138px 0px';
            }
        }
        else if(button.style.backgroundPostion != '')
        {
            button.style.backgroundPosition = '';
        }
    }
}

/*
    This function will toggle a button from one state to another (selected to deselected, deselected to selected).
    This is only a visual change.
*/
function ToggleButton(event)
{
    var button = Event.element(event);
    
    if(button != null)
    {
        if(button.className != selectedButtonClass)
        {
            ChangeMode(true);
            
            if(button.className == selectedButtonClass)
            {
                button.className = deselectedButtonClass;
            }
            else
            {
                button.className = selectedButtonClass;
            }
        }
    }
}

/*
    This function will change the mode the map is currently in. It will update the map
    if told to do so. This is not the greatest the function as it mixes a bunch of different
    functionality (changing UI, executing map positioning, etc.).
*/
function ChangeMode(updateMap)
{
    var buyOptions = $(buyOptionsID);
    var playOptions = $(playOptionsID);
    
    if(currentMode == 'buy')
    {
        buyOptions.style.display = 'none';
        playOptions.style.display = 'block';
        
        $(showBuyID).style.display = 'none';
        $(showPlayID).style.display = 'block';
        
        $(storeLocatorDateControlsID).style.display = 'block';
        
        currentMode = 'play';
    }
    else
    {
        buyOptions.style.display = 'block';
        playOptions.style.display = 'none';
        
        $(showBuyID).style.display = 'block';
        $(showPlayID).style.display = 'none';
        
        $(storeLocatorDateControlsID).style.display = 'none';
        
        currentMode = 'buy';
    }
    
    var button = $$('.' + selectedButtonClass);
    
    for(var i = 0; i < button.length; i++)
    {
        if(button[i].className == selectedButtonClass)
        {
            button[i].className = deselectedButtonClass;
        }
    }  
    
    $(resultsTableID).style.display = 'none';
    
    map.clearOverlays();
    
    if(updateMap)
    {    
        UpdateMap(true); 
    }
}

/*
    This function will position the map if the Return/Enter key was pressed
    while typing in the address box.
*/
function GetMapEnter(event)
{
    var keyCode = event.keyCode ? event.keyCode : event.which;  

    if(keyCode == 13)
    {          
        Event.stop(event);
        
        GetMap(event);
    }
}

/*
    This function will get the map if you click on the "GetMap" button
    that is on the address form. It just centers the map to the geocoded
    address (if it was successfully geocoded, otherwise does nothing).
*/
function GetMap(event)
{
    if(event != null)
    {
        Event.stop(event);
    }
    
    var address = $(addressTextBoxID).value;

    geocoder.getLatLng(address, function(point)
    { 
        if(point != null)
        {
            validSearchCondition = true;
            
            latitude = point.lat();
            longitude = point.lng();    	    
            
            var updateMap = point.toString() == map.getCenter().toString();
            var oldZoom = map.getZoom();
    	
	        // Set the map center which should invoke the event that populates
	        // it with pointers depending on which mode we're in. This is all
	        // we need to do right now.
	        map.setCenter(new google.maps.LatLng(latitude, longitude), 10);

	        if(updateMap || oldZoom == map.getZoom())
	        {
	            UpdateMap(false);
	        } 
        }        
    });    
}

/*
    This function will return a comma delimited list of all of the checkboxes based on the name
    you pass in, regardless of their checked state (unless you pass in true for 
    "needsToBeChecked"). You can provide a delimiter as well. If none is provided, ',' is 
    assumed.
*/
function GetCheckBoxes(name, needsToBeChecked, delimiter)
{
    var boxes = $$('input[name="' + name + '"]');
    var boxesList = '';
    
    if(delimiter == null)
    {
        delimiter = ',';
    }
    
    for(var i = 0; i < boxes.length; i++)
    {
        if(!needsToBeChecked || (needsToBeChecked && boxes[i].checked))
        {
            boxesList = boxesList + boxes[i].value + delimiter;
        }
    }
    
    return boxesList;
}

/*
    This function will check all the necessary checkboxes based on a 
    string array passed in which has the values of the ones that should
    be checked.
*/
function SetCheckedBoxes(name, checked)
{
    if(checked != null)
    {
        var boxes = $$('input[name="' + name + '"]');
        
        for(var i = 0; i < boxes.length; i++)
        {
            boxes[i].checked = false;
            
            for(var j = 0; j < checked.length; j++)
            {
                if(boxes[i].value == checked[j])
                {
                    boxes[i].checked = true;
                    
                    break;
                }
            }
        }
    }
}

/*
    This function will reset the map to its default state, effectively
    centering the map on the United States, setting all of the form
    checkboxes, wiping away the address text box value.
*/
function Reset()
{
    validSearchCondition = false;
    lastDirectionsQuery = null;
    currentMode = null;
    
    var boxes = $$('input[type="checkbox"]');
    
    for(var i = 0; i < boxes.length; i++)
    {
        boxes[i].checked = true;
    }
    
    ChangeMode(false);
    
    $(storeButtonID).className = selectedButtonClass;
    $(addressTextBoxID).value = '';
    $(startDateID).value = '';
    $(endDateID).value = '';
    
    map.setCenter(new google.maps.LatLng(map.DefaultLatitude, map.DefaultLongitude), map.DefaultZoom);
    
    visibleMarkers = new Hash();
}

/*
    This function will update the map with the latest markers, and clearing ones that
    no longer show.
*/
function UpdateMap(forceUpdate)
{
    var checkedProducts = GetCheckBoxes('product', true, ',');
    var checkedEvents = GetCheckBoxes('event', true, ',');
    var checkedEventBrands = GetCheckBoxes('eventbrand', true, ',');
    var startDateValue = $(startDateID).value;
    var endDateValue = $(endDateID).value;
    
    if(forceUpdate || lastBounds == null || lastBounds.Products == null || lastBounds.Events == null || 
        lastBounds.EventBrands == null || !lastBounds.containsBounds(map.getBounds()) || 
        lastBounds.Products.toString() != checkedProducts.toString() || lastBounds.Events.toString() != checkedEvents.toString() || 
        lastBounds.EventBrands.toString() != checkedEventBrands.toString() || startDateValue != lastBounds.StartDate || endDateValue != lastBounds.EndDate)
    {
        if(validSearchCondition)
        {
            lastBounds = map.getBounds();
            lastBounds.Products = checkedProducts;
            lastBounds.Events = checkedEvents;
            lastBounds.EventBrands = checkedEventBrands;
            lastBounds.StartDate = startDateValue;
            lastBounds.EndDate = endDateValue;
            
            var bounds = lastBounds;    
            var products = lastBounds.Products;
            var events = lastBounds.Events;
            var eventBrands = lastBounds.EventBrands;
            var northEast = bounds.getNorthEast();
            var southWest = bounds.getSouthWest();
            var northLatitude = northEast.lat();
            var northLongitude = northEast.lng();
            var southLatitude = southWest.lat();
            var southLongitude = southWest.lng();
            var center = bounds.getCenter();
            var productsFiltered = checkedProducts != GetCheckBoxes('product', false, ',');
                        
            if(latitude == null || longitude == null)
            {
                latitude = center.lat();
                longitude = center.lng();
            }
                
            progressBar.SetProgress(0);
            progressBar.SetProgress(.01);
            
            setTimeout( function() { UpdateProgressBarForAjaxRequest(progressBar, true); }, 999);
            
            new Ajax.Request(LocationService, {
                method: 'get',
                parameters: { Products: products, 
                              Events: events,
                              EventBrands: eventBrands,
                              Mode: currentMode, 
                              Latitude: center.lat(),
                              Longitude: center.lng(),
                              NorthLatitude: northLatitude, 
                              EastLongitude: northLongitude,
                              SouthLatitude: southLatitude,
                              WestLongitude: southLongitude,
                              StartLatitude: latitude,
                              StartLongitude: longitude,
                              ProductsFiltered: productsFiltered,
                              StartDate: startDateValue,
                              EndDate: endDateValue },
                onSuccess:  function(transport) {
                                var result = eval('(' + transport.responseText + ')');
                                
                                UpdateProgressBarForAjaxRequest(progressBar, false);
                                
                                currentLocations = null;
                                
                                if(result.Result)
                                {
                                    currentLocations = result.Locations;
                                    
                                    PlaceLocations(currentLocations, bounds);    
                                    UpdateLocationsTable(currentLocations, bounds);                        
                                }
                                else
                                {
                                    progressBar.SetProgress(1);   
                                
                                    messageContainer.setMessage(Resources.get('ProcessingErrorMessage'));
                                    messageContainer.show();
                                }                        
                            },
                onFailure:  function(transport) {
                                progressBar.SetProgress(1);   
                                
                                messageContainer.setMessage(Resources.get('ProcessingErrorMessage'));
                                messageContainer.show();
                            }
                }); 
            }
    }
    else
    {        
        ClearOverlays(null);
        
        lastBounds = map.getBounds();
        lastBounds.Products = GetCheckBoxes('product', true, ',');
        lastBounds.Events = GetCheckBoxes('event', true, ',');
    }
}

/*
    This function will artificially increase the progress on the progress bar for AJAX requests
    to complete. It will stop when the progress bar gets flagged as the ajax request being complete.
*/
function UpdateProgressBarForAjaxRequest(progressBar, continueOrNot)
{
    if(progressBar != null)
    {
        progressBar.shouldContinue = continueOrNot;
        
        if(progressBar.shouldContinue)
        {
            var newTempProgress = progressBar.GetProgress() + .01;
            
            if(newTempProgress < 1)
            {
                progressBar.SetProgress(newTempProgress);
            
                setTimeout(function() { UpdateProgressBarForAjaxRequest(progressBar, progressBar.shouldContinue); }, 999);
            }
            else if((newTempProgress - .01) < 1)
            {
                progressBar.SetProgress(1);   
                                
                messageContainer.setMessage(Resources.get('ProcessingErrorMessage'));
                messageContainer.show(); 
            }
        }
    }
}

/*
    This function will place the locations on the map once they come in. It does this asynchronously
    with setTimeout.
*/
function PlaceLocations(locations, bounds)
{
    if(map.getBounds().toString() == bounds.toString())
    {
        ClearOverlays(locations);
        
        setTimeout(function() { PlaceLocation(locations, 0); }, 0);
    }
}

/*
    This function will clear the overlays off the map that are no longer
    visible.
*/
function ClearOverlays(locations)
{               
    if(visibleMarkers != null)
    {
        var bounds = map.getBounds();
        var northEast = bounds.getNorthEast();
        var southWest = bounds.getSouthWest();
        
        if(locations != null)
        {
            var tempVisibleMarkers = new Hash();        

            for(var i = 0; i < locations.length; i++)
            {
                var newLocation = locations[i];
                var oldMarker = visibleMarkers.get(newLocation.ID);
                
                if(oldMarker != null)
                {   
                    newLocation.Marker = oldMarker;
                    
                    tempVisibleMarkers.set(newLocation.ID, oldMarker);
                    visibleMarkers.unset(newLocation.ID);
                }
            }
            
            visibleMarkers.each(function(pair) {
                var m = pair.value;
                
                if(m != null)
                {
                    map.removeOverlay(m);        
                }
            });
            
            visibleMarkers = tempVisibleMarkers;
        }
        
        visibleMarkers.each(function(pair) {
            var m = pair.value;
            
            if(m != null)
            {
                var p = m.getPoint();
                
                if (!((p.lat() < northEast.lat()) && (p.lat() > southWest.lat()) && (p.lng() < northEast.lng()) && (p.lng() > southWest.lng()))) 
                {
			        map.removeOverlay(m);
			        
			        visibleMarkers.unset(pair.key);
                }            
            }
        });
    }    
}

/*
    This function will place a location on the map and subscribe to the necessary events
    to open a marker window at the location. This function also updates the progress bar.
*/
function PlaceLocation(locations, index)
{
    if(index < locations.length && locations == currentLocations)
    {
        var end = index + 10;
        
        for(var i = index; i < end && i < locations.length && locations == currentLocations; i++)
        {
            var location = locations[i];  
            
            if(visibleMarkers.get(location.ID) == null)
            {
                var marker = new google.maps.Marker(new google.maps.LatLng(location.Latitude, location.Longitude), icons.get(location.LocationType), false);
                
                marker.Location = location; 
                location.Marker = marker;       
                            
                map.addOverlay(marker);
                
                visibleMarkers.set(location.ID, marker);
                
                google.maps.Event.addListener(marker, 'click', DisplayLocationInfoWindow.bindAsEventListener(this, marker, location, i));
                
                if(location.ID == locationIDToOpen)
                {
                    DisplayLocationInfoWindow(this, marker, location, i);
                    
                    locationIDToOpen = null;
                }
            }
        }
        
        progressBar.SetProgress((end + 2) / locations.length);

        setTimeout(function() { PlaceLocation(locations, end); }, 0);
    }
    else if(locations == currentLocations)
    {
        progressBar.SetProgress(1);
    }
}

/*
    This function generates directions controls which can be placed
    inside a container. Once created, it subscribes to a couple events
    for click and keypress.
*/
function GetDirectionsControls(toHere, fromHere, point, location, update) 
{
    var directionsControls = new Element('div', { 'style': 'display:none;' });
    var labelSpan = new Element('span', { 'style': 'font-size:.75em; font-weight:normal; color: #434343;' });
    var directionsBack = new Element('a', { 'href': 'javascript:void(0);', 'style': 'font-size:.75em; color:blue;' }).update(Resources.get('Back'));
    var directionsInput = new Element('input', { 'type': 'text', 'maxlength': '150', 'style': 'width:150px;' });
    var directionsButton = new Element('input', { 'type': 'button', 'value': Resources.get('Go'), 'class': storeLocatorGoButtonClass }).addClassName(storeLocatorGoButtonClass);

    directionsControls.insert(labelSpan);
    directionsControls.insert(new Element('br'));
    directionsControls.insert(directionsInput);
    directionsControls.insert(directionsButton);    
    directionsControls.insert('&nbsp;');
    directionsControls.insert(directionsBack);
    
    directionsControls.ToHere = toHere;
    directionsControls.FromHere = fromHere;
    directionsControls.Input = directionsInput;
    directionsControls.Label = labelSpan;
    directionsControls.Update = update;
    
    labelSpan.IsStart = true;
    
    Event.observe(directionsButton, 'click', GetDirections.bindAsEventListener(this, directionsButton, directionsInput, point, labelSpan, location));
    Event.observe(toHere, 'click', DisplayDirectionsControls.bindAsEventListener(this, toHere, fromHere, directionsControls, labelSpan, directionsInput, update, toHere.innerHTML));
    Event.observe(fromHere, 'click', DisplayDirectionsControls.bindAsEventListener(this, toHere, fromHere, directionsControls, labelSpan, directionsInput, update, fromHere.innerHTML));
    Event.observe(directionsBack, 'click', DisplayDirectionsControls.bindAsEventListener(this, toHere, fromHere, directionsControls, labelSpan, directionsInput, update, directionsBack.innerHTML));
    Event.observe(directionsInput, 'keypress', GetDirections.bindAsEventListener(this, directionsButton, directionsInput, point, labelSpan, location));
    
    return directionsControls;
}

/*
    This function will open up the location marker info window. It generates all the markup objects
    necessary to show the marker window depending on what type of location it is. 
*/
function DisplayLocationInfoWindow(control, marker, location, currentLocationsIndex)
{
    var point = new google.maps.LatLng(location.Latitude, location.Longitude);
    
    if(location == null)
    {
        location = marker.Location;
    }
    
    if(marker == null)
    {
        marker = location.Marker;
    }
    
    if(marker != null)
    {
        point = marker.getPoint();
    }
    
    var newLocation = new Element('div', { 'class': storeLocatorBubbleClass + location.LocationType }).addClassName(storeLocatorBubbleClass + location.LocationType);
    var newLocationName = new Element('span').update(location.Name);
    var newLocationInfo = new Element('div');
    
    if(location.AddressLine1 != null && location.AddressLine1 != '')
    {
        newLocationInfo.insert(location.AddressLine1);
        
        if(location.Distance != null && location.Distance != '')
        {
            newLocationInfo.insert('&nbsp;(');
            newLocationInfo.insert(new Element('b').update(location.Distance));
            newLocationInfo.insert('&nbsp;' + Resources.get('MilesLabel') + ')');
        }
        
        newLocationInfo.insert(new Element('br'));
    }
    
    if(location.AddressLine2 != null && location.AddressLine2 != '')
    {
        newLocationInfo.insert(location.AddressLine2);        
        newLocationInfo.insert(new Element('br'));
    }
    
    var includeBreak = false;
    
    if(location.City != null && location.City != '')
    {
        newLocationInfo.insert(location.City);
        
        includeBreak = true;
    }
    
    if(location.State != null && location.State != '')
    {
        if(includeBreak)
        {
            newLocationInfo.insert(',&nbsp;');        
        }
        
        newLocationInfo.insert(location.State);        
        
        includeBreak = true;
    }
    
    if(location.PostalCode != null && location.PostalCode != '')
    {
        newLocationInfo.insert('&nbsp;' + location.PostalCode);        
        
        includeBreak = true;
    }
    
    if(location.Country != null && location.Country != '')
    {
        if(includeBreak)
        {
            newLocationInfo.insert(new Element('br'));  
        }
        else
        {
            newLocationInfo.insert('&nbsp;');
        }
        
        newLocationInfo.insert(location.Country);        
        
        includeBreak = true;
    }
    
    if(includeBreak)
    {
        newLocationInfo.insert(new Element('br'));
    }
    
    if(location.Phone != null && location.Phone != '')
    {
        newLocationInfo.insert(location.Phone);
        newLocationInfo.insert(new Element('br'));
    }
    
    /*
    if(location.Email != null && location.Email != '')
    {
        var storeEmailLink = new Element('a', { 'href': 'mailto:' + location.Email }).update(location.Email);
        
        newLocationInfo.insert(storeEmailLink);
        newLocationInfo.insert(new Element('br'));
    } */
    
    if(location.Website != null && location.Website != '')
    {
        var storeWebsiteLink = new Element('a', { 'class': storeLocatorWebsiteClass, 'href': 'http://' + location.Website }).addClassName(storeLocatorWebsiteClass).update(location.Website);
        
        storeWebsiteLink.observe('click', function(event) {
            var element = Event.element(event);
            
            window.open('http://' + element.innerHTML);
            
            Event.stop(event);
        });                        
        
        newLocationInfo.insert(storeWebsiteLink);
    }        
    
    var directionsSpan = new Element('div', { 'class': storeLocatorDirectionsLinkClass }).addClassName(storeLocatorDirectionsLinkClass);
    var toHere = new Element('a', { 'href': 'javascript:void(0);', 'style': 'color:blue;' }).update(Resources.get('ToHereLabel'));
    var fromHere = new Element('a', { 'href': 'javascript:void(0);', 'style': 'color:blue;' }).update(Resources.get('FromHereLabel'));
    var locationInfoPreDirections = newLocationInfo.innerHTML;
    var directionsControls = GetDirectionsControls(toHere, fromHere, point, location, true);
    
    directionsSpan.insert(new Element('br'));
    directionsSpan.insert(Resources.get('GetDirectionsLabel') + ': ');
    directionsSpan.insert(toHere);
    directionsSpan.insert('&nbsp;-&nbsp;');
    directionsSpan.insert(fromHere);
    directionsSpan.insert(directionsControls);
    
    marker.DirectionsControls = directionsControls;    
    
    newLocationInfo.insert(directionsSpan);
    
    newLocation.insert(newLocationName);
    newLocation.insert(newLocationInfo);
                
    var maximizedParent = new Element('div', { 'class': storeLocatorMaxClass }).addClassName(storeLocatorMaxClass);
    var intersectionImage = new Element('img', { 'class': storeLocatorMaxMapClass, 'src': 'http://maps.google.com/staticmap?markers=' + location.Latitude + ',' + location.Longitude + ',midred&center=' + location.Latitude + ',' + location.Longitude + '&zoom=14&size=200x100&sensor=false&key=' + StaticImageKey, alt: Resources.get('LocationAltText') }).addClassName(storeLocatorMaxMapClass);
    
    maximizedParent.insert(new Element('br'));
    maximizedParent.insert(intersectionImage);       
    maximizedParent.insert(new Element('div', { 'class': storeLocatorBubbleAddressClass + location.LocationType }).addClassName(storeLocatorBubbleAddressClass + location.LocationType).update(locationInfoPreDirections));      
    
    directionsSpan = new Element('div', { 'class': storeLocatorDirectionsLinkClass }).addClassName(storeLocatorDirectionsLinkClass);
    toHere = new Element('a', { 'href': 'javascript:void(0);', 'style': 'color:blue;' }).update(Resources.get('ToHereLabel'));
    fromHere = new Element('a', { 'href': 'javascript:void(0);', 'style': 'color:blue;' }).update(Resources.get('FromHereLabel'));
    
    directionsControls = GetDirectionsControls(toHere, fromHere, point, location, false);
    
    directionsSpan.insert(new Element('br'));
    directionsSpan.insert(Resources.get('GetDirectionsLabel') + ': ');
    directionsSpan.insert(toHere);
    directionsSpan.insert('&nbsp;-&nbsp;');
    directionsSpan.insert(fromHere);
    directionsSpan.insert(directionsControls);
    
    maximizedParent.insert(directionsSpan);
        
    if(location.Products != null && location.Products.length > 0)
    {
        var products = new Element('div', { 'class': storeLocatorMaximizedProductsClass}).addClassName(storeLocatorMaximizedProductsClass);        
        var productList = new Element('ul');
        
        for(var i = 0; i < location.Products.length; i++)
        {
            productList.insert('<li>' + location.Products[i] + '</li>');
        }
        
        products.insert(productList);
        
        /*
        products.insert(new Element('br'));
        products.insert(new Element('div').update(new Element('i').update(Resources.get('AvailabilityNotice'))));
        */

        maximizedParent.insert(new Element('br'));
        maximizedParent.insert(new Element('span').update(Resources.get('ProductsLabel')));
        maximizedParent.insert(products); 
    }
    
    if(location.LocationType == 'Venue')        
    {
        marker.DirectionsControls = directionsControls;
        
        var products = new Element('div', { 'class': storeLocatorMaximizedEventsClass }).addClassName(storeLocatorMaximizedEventsClass);        
        var productList = new Element('ul');
        
        products.insert(productList);

        maximizedParent.insert(new Element('br'));
        maximizedParent.insert(new Element('span').update(Resources.get('EventsLabel')));
        maximizedParent.insert(products);  
                                
        var bounds = lastBounds;    
        var products = GetCheckBoxes('product', true, ',');
        var events = GetCheckBoxes('event', true, ',');
        var eventBrands = GetCheckBoxes('eventbrand', true, ',');
        var northEast = bounds.getNorthEast();
        var southWest = bounds.getSouthWest();
        var northLatitude = northEast.lat();
        var northLongitude = northEast.lng();
        var southLatitude = southWest.lat();
        var southLongitude = southWest.lng();
        var center = bounds.getCenter();
        var startDateValue = $(startDateID).value;
        var endDateValue = $(endDateID).value;
        
        productList.update(new Element('img', { 'src': '/StoreAndEventLocator/Images/loading.gif', 'alt': Resources.get('LoadingEventsAltText') }));
            
        new Ajax.Request(LocationService, {
                method: 'get',
                parameters: { 
                              VenueID: location.ID,
                              Products: products, 
                              Events: events,
                              EventBrands: eventBrands,
                              Mode: 'events', 
                              Latitude: center.lat(),
                              Longitude: center.lng(),
                              NorthLatitude: northLatitude, 
                              EastLongitude: northLongitude,
                              SouthLatitude: southLatitude,
                              WestLongitude: southLongitude,
                              StartLatitude: latitude,
                              StartLongitude: longitude,
                              StartDate: startDateValue,
                              EndDate: endDateValue},
                onSuccess:  function(transport) {
                                var result = eval('(' + transport.responseText + ')');
                                
                                productList.update();
                                
                                if(result.Events.length == 0)
                                {
                                    productList.update(new Element('li').update(Resources.get('NoUpcomingEvents')));
                                }
                                
                                var lastEventSearchType = true;
                                
                                for(var i = 0; i < result.Events.length; i++)
                                {
                                    var eventData = result.Events[i];
                                    var eventInfo = new Element('li');
                                    
                                    eventInfo.insert(new Element('b').update(Resources.get('NameLabel') + ': '));
                                    eventInfo.insert(eventData.Name);
                                    eventInfo.insert(new Element('br'));  
                                    
                                    if(eventData.Format != null && eventData.Format != '')
                                    {
                                        eventInfo.insert(new Element('b').update(Resources.get('FormatLabel') + ': '));
                                        eventInfo.insert(eventData.Format);
                                        eventInfo.insert(new Element('br'));  
                                    }
                                    
                                    if(eventData.IsMainSearchType != lastEventSearchType && i != 0)
                                    {
                                        productList.insert(new Element('li', { 'style': 'font-size:1.1em;' }).update('<b>' + Resources.get('OtherEventsAtLocation') + '</b>')); 
                                    }
                                    
                                    lastEventSearchType = eventData.IsMainSearchType;
                                    
                                    /*
                                    eventInfo.insert(new Element('b').update(Resources.get('SanctionNumberLabel') + ': '));
                                    eventInfo.insert(eventData.SanctionNumber);
                                    eventInfo.insert(new Element('br')); 
                                    */
                                    
                                    if(eventData.EventBrand != null && eventData.EventBrand != '')
                                    {
                                        eventInfo.insert(new Element('b').update(Resources.get('ProductLabel') + ': '));
                                        eventInfo.insert(eventData.EventBrand);
                                        eventInfo.insert(new Element('br')); 
                                    }
                                    
                                    if(eventData.PhoneNumber != null && eventData.PhoneNumber != '')
                                    {
                                        eventInfo.insert(new Element('b').update(Resources.get('PhoneNumberLabel') + ': '));
                                        eventInfo.insert(eventData.PhoneNumber);
                                        eventInfo.insert(new Element('br')); 
                                    }
                                    
                                    if(eventData.Email != null && eventData.Email != '')
                                    {
                                        eventInfo.insert(new Element('b').update(Resources.get('EmailLabel') + ': '));
                                        eventInfo.insert(new Element('a', { 'href': 'mailto:' + eventData.Email }).update(eventData.Email));
                                        eventInfo.insert(new Element('br'));                                    
                                    }
                                    
                                    if(eventData.Url != null && eventData.Url != '')
                                    {
                                        eventInfo.insert(new Element('b').update(Resources.get('UrlLabel') + ': '));
                                        eventInfo.insert(new Element('a', { 'href': eventData.Url }).update(eventData.Url));
                                        eventInfo.insert(new Element('br'));                                    
                                    }
                                    
                                    if(eventData.Coordinator != null && eventData.Coordinator != '')
                                    {
                                        eventInfo.insert(new Element('b').update(Resources.get('CoordinatorLabel') + ': '));

                                        if(eventData.Coordinator.indexOf('@') >= 0)
                                        {
                                            eventInfo.insert(new Element('a', { 'href': 'mailto:' + eventData.Coordinator }).update(eventData.Coordinator));
                                        }
                                        else
                                        {
                                            eventInfo.insert(eventData.Coordinator);
                                        }
                                        
                                        eventInfo.insert(new Element('br')); 
                                    }
                                    
                                    if(eventData.StartDate != null && eventData.StartDate != '')
                                    {
                                        eventInfo.insert(new Element('b').update(Resources.get('DateLabel') + ': '));
                                        eventInfo.insert(eventData.StartDate);
                                        
                                        if(eventData.EndDate != null && eventData.EndDate != '' && eventData.EndDate != eventData.StartDate)
                                        {
                                            eventInfo.insert('&nbsp;-&nbsp;' + eventData.EndDate);
                                        }
                                        
                                        eventInfo.insert(new Element('br')); 
                                    }
                                    
                                    productList.insert(eventInfo);                                  
                                }                 
                            }
                }); 
    }
    
    if(location.LocationType == 'Venue')
    {
        map.openInfoWindow(point, newLocation, { maxContent: maximizedParent, maxTitle: new Element('span', { 'class': storeLocatorBubbleTitleClass }).addClassName(storeLocatorBubbleTitleClass).update(location.Name), onOpenFn: function() {
                map.getInfoWindow().maximize();                    
            }
        });         
    }
    else
    {
        map.openInfoWindow(point, newLocation, { maxContent: maximizedParent, maxTitle: new Element('span', { 'class': storeLocatorBubbleTitleClass } ).addClassName(storeLocatorBubbleTitleClass).update(location.Name) });            
    }
    
    lastInfoWindowID = location.ID;
}

/*
    This function loads the directions onto the map and displays the directions
    div.
*/
function GetDirections(event, button, address, point, label, location)
{
    var performSearch = false;
    
    if(event.type == 'keypress')
    {
        var keyCode = event.keyCode ? event.keyCode : event.which;  

        if(keyCode == 13)
        {         
            performSearch = true;
            
            Event.stop(event);
        }
    } 
    else if(event.type == 'click')
    {
        performSearch = true;
    }
    
    if(performSearch)
    {
        var query = null;
        var title = null;
        var directionsForm = $(storeLocatorDirectionsFormID);
        var searchForm = $(storeLocatorSearchFormID);
        var tableResults = $(resultsTableID);
        var returnLinks = $(storeLocatorDirectionsFormLinksID);
        
        if(currentDirections != null)
        {
            currentDirections.clear();
            currentDirections = null;
            
            directionsForm.update();
        }   
        
        if(returnLinks != null)
        {
            returnLinks.remove();
        }
        
        if(label.IsStart)
        {
            query = 'to: ' + point.lat() + ',' + point.lng() + ' from: ' + address.value;
    		
	        currentDirections = new google.maps.Directions(map, directionsForm);
    		
	        title = new Element('h3').update(Resources.get('DrivingDirectionsToLabel') + ' ' + location.Name);
        }
        else
        {
            query = 'from: ' + point.lat() + ',' + point.lng() + ' to: ' + address.value;
    		
	        currentDirections = new google.maps.Directions(map, directionsForm);
    		
	        title = new Element('h3').update(Resources.get('DrivingDirectionsFromLabel') + ' ' + location.Name);
        }
        
        lastDirectionsQuery = query;
        
        google.maps.Event.addListener(currentDirections, 'load', function() { 
            returnLinks = new Element('div', { 'id': storeLocatorDirectionsFormLinksID });
            
            var startNewSearch = new Element('a', { 'href': 'javascript:void(0);' }).update(Resources.get('StartNewSearch'));
            var backToResults = new Element('a', { 'href': 'javascript:void(0);' }).update(Resources.get('BackToResults'));

            returnLinks.insert(startNewSearch);
            returnLinks.insert(backToResults);
            
            directionsForm.insert({ after: returnLinks });
            
            Event.observe(startNewSearch, 'click', function() {
                returnLinks.remove();
                directionsForm.update();
                
                directionsForm.style.display = 'none';
                searchForm.style.display = 'block';
                
                Reset();
            });
                   
            Event.observe(backToResults, 'click', BackToResults.bindAsEventListener(this, currentDirections, directionsForm, tableResults, searchForm, returnLinks));
        });   
        
        google.maps.Event.addListener(currentDirections, 'error', function() { 
            returnLinks = new Element('div', { 'id': storeLocatorDirectionsFormLinksID });
            
            var errorMessage = new Element('div', { 'class': storeLocatorAddressNotLocated }).addClassName(storeLocatorAddressNotLocated).update(Resources.get('LocationNotFound'));
            var startNewSearch = new Element('a', { 'href': 'javascript:void(0);' }).update(Resources.get('StartNewSearch'));
            var backToResults = new Element('a', { 'href': 'javascript:void(0);' }).update(Resources.get('BackToResults'));

            returnLinks.insert(startNewSearch);
            returnLinks.insert(backToResults);
            
            directionsForm.insert(errorMessage);
            directionsForm.insert({ after: returnLinks });
            
            Event.observe(startNewSearch, 'click', function() {
                returnLinks.remove();
                directionsForm.update();

                directionsForm.style.display = 'none';
                searchForm.style.display = 'block';
                
                Reset();
            });
                   
            Event.observe(backToResults, 'click', BackToResults.bindAsEventListener(this, currentDirections, directionsForm, tableResults, searchForm, returnLinks));
        });
        
        currentDirections.load(query);
        
        directionsForm.insert({ top: title });
        directionsForm.style.display = 'block';
        searchForm.style.display = 'none';
        tableResults.style.display = 'none';
    }
}

/*
    This function loads the directions onto the map if you just have a 
    query already (like if we're rebuilding map state).
*/
function LoadDirections(newQuery)
{
    if(newQuery != null && newQuery != '')
    {
        var query = newQuery;
        var title = null;
        var directionsForm = $(storeLocatorDirectionsFormID);
        var searchForm = $(storeLocatorSearchFormID);
        var tableResults = $(resultsTableID);
        var returnLinks = $(storeLocatorDirectionsFormLinksID);
        
        if(currentDirections != null)
        {
            currentDirections.clear();
            currentDirections = null;
            
            directionsForm.update();
        }   
        
        if(returnLinks != null)
        {
            returnLinks.remove();
        }       
       
        currentDirections = new google.maps.Directions(map, directionsForm);
        title = new Element('h3').update(Resources.get('DrivingDirectionsLabel'));
        
        google.maps.Event.addListener(currentDirections, 'load', function() { 
            returnLinks = new Element('div', { 'id': storeLocatorDirectionsFormLinksID });
            
            var startNewSearch = new Element('a', { 'href': 'javascript:void(0);' }).update(Resources.get('StartNewSearch'));
            var backToResults = new Element('a', { 'href': 'javascript:void(0);' }).update(Resources.get('BackToResults'));

            returnLinks.insert(startNewSearch);
            returnLinks.insert(backToResults);
            
            directionsForm.insert({ after: returnLinks });
            
            Event.observe(startNewSearch, 'click', function() {
                returnLinks.remove();
                directionsForm.update();
                
                searchForm.style.display = 'block';
                
                Reset();
            });
                   
            Event.observe(backToResults, 'click', BackToResults.bindAsEventListener(this, currentDirections, directionsForm, tableResults, searchForm, returnLinks));
        });          
        
        google.maps.Event.addListener(currentDirections, 'error', function() { 
            returnLinks = new Element('div', { 'id': storeLocatorDirectionsFormLinksID });
            
            var errorMessage = new Element('div', { 'class': storeLocatorAddressNotLocated }).addClassName(storeLocatorAddressNotLocated).update(Resources.get('LocationNotFound'));
            var startNewSearch = new Element('a', { 'href': 'javascript:void(0);' }).update(Resources.get('StartNewSearch'));
            var backToResults = new Element('a', { 'href': 'javascript:void(0);' }).update(Resources.get('BackToResults'));

            returnLinks.insert(startNewSearch);
            returnLinks.insert(backToResults);
            
            directionsForm.insert(errorMessage);
            directionsForm.insert({ after: returnLinks });
            
            Event.observe(startNewSearch, 'click', function() {
                returnLinks.remove();
                directionsForm.update();
                
                searchForm.style.display = 'block';
                
                Reset();
            });
                   
            Event.observe(backToResults, 'click', BackToResults.bindAsEventListener(this, currentDirections, directionsForm, tableResults, searchForm, returnLinks));
        });

        currentDirections.load(query);
        
        directionsForm.insert({ top: title });
        directionsForm.style.display = 'block';
        searchForm.style.display = 'none';
        tableResults.style.display = 'none';
    }
}

/*
    This function returns the user back to their results depending on what they had entered into the address bar.
    So, it resets the directions, and returns them to their origional address query.
*/
function BackToResults(event, directionsToClear, directionsFormToClear, tableResultsToShow, searchFormToShow, returnLinksToRemove)
{
    GetMap(null);
                
    directionsToClear.clear();
    
    directionsFormToClear.update();
    
    tableResultsToShow.style.display = 'block';
    directionsFormToClear.style.display = 'none';
    searchFormToShow.style.display = 'block';
    
    returnLinksToRemove.remove();
    
    var shownInfoWindow = map.getInfoWindow();
    
    if(shownInfoWindow != null)
    {
        shownInfoWindow.hide();
    }
}

/*
    This function will display the directions controls (the links, input box, button, etc.) in the control
    container you provide as well as update the label text.
*/
function DisplayDirectionsControls(event, toHere, fromHere, controlsContainer, label, input, update, linkText)
{   
    var control = Event.element(event);
    var updateWindow = false;
    
    input.value = '';
    
    if(linkText != toHere.innerHTML && linkText != fromHere.innerHTML)
    {  
        updateWindow = true;
        
        controlsContainer.style.display = 'none'; 
        
        toHere.style.textDecoration = 'none';
        fromHere.style.textDecoration = 'none';
    }
    else
    {
        input.value = $(addressTextBoxID).value;
        
        if(linkText == toHere.innerHTML)
        {
            label.IsStart = true;
            label.update(Resources.get('StartAddress') + ':');
            fromHere.style.textDecoration = 'none';
            toHere.style.textDecoration = 'underline';
        }        
        else
        {
            label.IsStart = false;
            label.update(Resources.get('EndAddress') + ':');
            toHere.style.textDecoration = 'none';
            fromHere.style.textDecoration = 'underline';
        }
        
        if(controlsContainer.style.display == 'none')
        {
            controlsContainer.style.display = 'block'; 
            
            updateWindow = true;
        }
    }   

    if(updateWindow && update)
    {
        map.updateInfoWindow();  
    }
}

/*
    This function asynchronously updates the locations table with new locations.
*/
function UpdateLocationsTable(locations, bounds)
{
    if(map.getBounds().toString() == bounds.toString())
    {
        setTimeout(function() { UpdateLocationsTable(locations, bounds, 0); }, 0);
    }
}

/*
    This function updates the locations table based on a index you
    provide. It does a little more work than the other function with the same name
    in that it kicks off the update of the paging controls as well.
*/
function UpdateLocationsTable(locations, bounds, index)
{
    if(map.getBounds().toString() == bounds.toString())
    {
        UpdateLocationsTablePagingControls(locations.length, null);
        UpdateLocationsTableLocations(0);
    } 
}

/*
    This function actually performs the logic to update the results digest
    which displays below the map. It performs all the creating of markup
    and controls.
*/
function UpdateLocationsTableLocations(currentPage)
{
    if(currentLocations != null)
    {
        var start = currentPage * resultsPerPage;
        var end = (currentPage + 1) * resultsPerPage;
        
        var resultsTable = $(resultsTableID);

        if(currentLocations.length > start)
        {      
            var mainLocationContainer = $$('.' + storeLocatorResultLocationsClass)[0];      
            var mainLocationContainerStatus = $$('.' + storeLocatorResultLocationsStatusClass)[0];
            var mainLocationResultsText = $(storeLocatorPagingResultsTextID);
            
            if(mainLocationContainer != null)
            {
                resultsTable.style.display = 'block';
                mainLocationContainerStatus.style.display = 'block';
                mainLocationContainerStatus.update(tableProgressBar.create());
                mainLocationContainerStatus.insert({ top: new Element('span', { 'class': storeLocatorStatusTextClass }).addClassName(storeLocatorStatusTextClass).update(Resources.get('UpdatingLabel')) });
                
                tableProgressBar.Observe('fadeComplete', function() {
                    mainLocationContainerStatus.style.display = 'none';
                });
                
                if(mainLocationResultsText != null)
                {
                    var realEnd = end;
                    
                    if(end > currentLocations.length)
                    {
                        realEnd = currentLocations.length;
                    }
                    
                    mainLocationResultsText.update(Resources.get('ResultsLabel') + ' ' + (start + 1) + ' - ' + realEnd + ' ' + Resources.get('OfAboutLabel') + ' ' + currentLocations.length);
                }
                
                // Update the progress bar and call the next batch to add to the digest view.
                tableProgressBar.SetProgress(0);
                
                var tempDiv = new Element('div');
                                
                setTimeout(function() { UpdateLocationsTableLocation(mainLocationContainer, tempDiv, start, end, start); }, 100);
            }
        }
        else if(resultsTable != null)
        {
            resultsTable.style.display = 'none';
        }
    }
}

/*
    This function opens the directions for a bubble from the results digest.
*/
function OpenDirectionsFromLocationsTable(control, marker, location, currentLocationsIndex)
{
    var link = Event.element(control);
    
    DisplayLocationInfoWindow(control, marker, location, currentLocationsIndex);
    
    var directionsControls = marker.DirectionsControls;
    
    var toHere = directionsControls.ToHere;
    var fromHere = directionsControls.FromHere;
    var input = directionsControls.Input;
    var label = directionsControls.Label;
    var update = directionsControls.Update;

    DisplayDirectionsControls(control, toHere, fromHere, directionsControls, label, input, update, link.innerHTML);
}

/*
    This function updates the table location area and generates the HTML controls to display
    each location digest.
*/
function UpdateLocationsTableLocation(mainLocationContainer, tempDiv, start, end, index)
{
    if(index < currentLocations.length)
    {
        var localEnd = index + 10;
        
        for(var i = index; i < localEnd && i < end && i < currentLocations.length; i++)
        {
            var location = currentLocations[i];
            var newLocationTitle = new Element('a', { 'href': 'javascript:void(0);' }).update(location.Name);
            var newLocation = new Element('div', { 'class': storeLocatorLocationClass + location.LocationType }).addClassName(storeLocatorLocationClass + location.LocationType);
            var newLocationName = new Element('span').update(newLocationTitle);
            var newLocationInfo = new Element('div');
            
            Event.observe(newLocationTitle, 'click', DisplayLocationInfoWindow.bindAsEventListener(this, location.Marker, location, i));
            
            if(location.AddressLine1 != null && location.AddressLine1 != '')
            {
                newLocationInfo.insert(location.AddressLine1);
                
                if(location.Distance != null && location.Distance != '')
                {
                    newLocationInfo.insert('&nbsp;(');
                    newLocationInfo.insert(new Element('b').update(location.Distance));
                    newLocationInfo.insert('&nbsp;' + Resources.get('MilesLabel') + ')');
                }
                
                newLocationInfo.insert(new Element('br'));
            }
            
            var includeBreak = false;
    
            if(location.City != null && location.City != '')
            {
                newLocationInfo.insert(location.City);
                
                includeBreak = true;
            }
            
            if(location.State != null && location.State != '')
            {
                if(includeBreak)
                {
                    newLocationInfo.insert(',&nbsp;');        
                }
                
                newLocationInfo.insert(location.State);        
                
                includeBreak = true;
            }
            
            if(location.PostalCode != null && location.PostalCode != '')
            {
                newLocationInfo.insert('&nbsp;' + location.PostalCode);        
                
                includeBreak = true;
            }
            
            if(includeBreak)
            {
                newLocationInfo.insert(new Element('br'));
            }
            
            if(location.Phone != null && location.Phone != '')
            {
                newLocationInfo.insert(location.Phone);
                newLocationInfo.insert(new Element('br'));
            }
            
            if(location.Website != null && location.Website != '')
            {
                var storeWebsiteLink = new Element('a', { 'class': storeLocatorWebsiteClass, 'href': 'http://' + location.Website }).addClassName(storeLocatorWebsiteClass).update(location.Website);
                
                storeWebsiteLink.observe('click', function(event) {
                    var element = Event.element(event);
                    
                    window.open('http://' + element.innerHTML);
                    
                    Event.stop(event);
                });                        
                
                newLocationInfo.insert(storeWebsiteLink);
            }
            
            var directionsSpan = new Element('div', { 'class': storeLocatorDirectionsLinkClass }).addClassName(storeLocatorDirectionsLinkClass);
            var toHere = new Element('a', { 'href': 'javascript:void(0);' }).update(Resources.get('ToHereLabel'));
            var fromHere = new Element('a', { 'href': 'javascript:void(0);' }).update(Resources.get('FromHereLabel'));
            
            Event.observe(toHere, 'click', OpenDirectionsFromLocationsTable.bindAsEventListener(this, location.Marker, location, i));
            Event.observe(fromHere, 'click', OpenDirectionsFromLocationsTable.bindAsEventListener(this, location.Marker, location, i));            
            
            directionsSpan.insert(Resources.get('GetDirectionsLabel') + ': ');
            directionsSpan.insert(toHere);
            directionsSpan.insert('&nbsp;-&nbsp;');
            directionsSpan.insert(fromHere);
            
            newLocationInfo.insert(directionsSpan);
            
            newLocation.insert(newLocationName);
            newLocation.insert(newLocationInfo);
            
            tempDiv.insert(newLocation);
        }
        
        tableProgressBar.SetProgress(localEnd / end);
            
        setTimeout(function() { UpdateLocationsTableLocation(mainLocationContainer, tempDiv, start, end, localEnd); }, 100);
    }
    else
    {
        mainLocationContainer.update(tempDiv);
        
        tableProgressBar.SetProgress(1);
    }
}

/*
    This function updates the paging controls for the
    results digest table. The paging controls allow the user to page through
    the results.
*/
function UpdateLocationsTablePagingControls(count, currentPage)
{
    var pagingContainers = $$('.' + storeLocatorPagingControlsClass);
    
    for(var j = 0; j < pagingContainers.length; j++)
    {
        pagingContainers[j].update();    
    }
    
    var pages = Math.ceil(count / resultsPerPage);
    var maxPages = 10;
    var index = 0;
    var start = 0;
    var end = 0;
    var prevPage = null;
    var nextPage = null;
    
    if(currentPage != null)
    {
        end = currentPage + maxPages > pages ? pages : currentPage + maxPages;
        start = pages < maxPages ? 0 : end - maxPages;
    }
    else
    {
        end = index + maxPages > pages ? pages : index + maxPages;
    }    
    
    for(var i = start; i < pages && i < end && pages > 1; i++)
    {
        var page = i + 1;
        var displayPage = i;
        var href = '?' + displayPage;
        var className = null;
        
        if((currentPage == null && page == 1) || parseInt(currentPage) == displayPage)
        {
            className = 'selected';
            
            if(currentPage == null && page == 1)
            {
                prevPage = 0;
                nextPage = 1;
            }
            else if(parseInt(currentPage) == displayPage)
            {
                prevPage = parseInt(currentPage) - 1;
                nextPage = parseInt(currentPage) + 1;
                
                if(prevPage < 0)
                {
                    prevPage = 0;
                }
                
                if(nextPage >= pages)
                {
                    nextPage = pages - 1;
                }
            }
        }
        
        AddLinkToPagingContainer(pagingContainers, page, null, className, PagingControlClick, i);   
    }
    
    if(pages > 1)
    {
        if(prevPage != null)
        {
            AddLinkToPagingContainer(pagingContainers, Resources.get('PreviousLabel'), 'top', null, PagingControlClick, prevPage);
        }
        
        if(nextPage != null)
        {
            AddLinkToPagingContainer(pagingContainers, Resources.get('NextLabel'), 'bottom', null, PagingControlClick, nextPage);
        }
        
        AddLinkToPagingContainer(pagingContainers, '', 'top', storeLocatorPreviousClass, PagingControlClick, 0);
        AddLinkToPagingContainer(pagingContainers, '', 'bottom', storeLocatorNextClass, PagingControlClick, pages - 1);
    }
}

/*
    This function adds a link to a paging container. The reason this is necessary is because
    there are two sets of paging controls and they both must have the links added to them. You can't
    add a link to multiple containers because a HTML control can only be in one container
    at a time.
*/
function AddLinkToPagingContainer(containers, page, pos, cssClass, eventDelegate, params)
{
    for(var j = 0; j < containers.length; j++)
    {
        var link = new Element('a', { 'href': 'javascript:void(0);' }).update(page);
        var pagingContainer = containers[j];
        
        if(cssClass != null)
        {
            link.className = cssClass;
        }
        
        Event.observe(link, 'click', eventDelegate.bindAsEventListener(this, params));
        
        switch(pos)
        {
            case 'top':
                pagingContainer.insert({ top: link });        
                break;
            case 'bottom':
                pagingContainer.insert({ bottom: link });
                break;
            default:
                pagingContainer.insert(link);
                break;
        }           
    }     
}

/*
    This function is called when a paging control is clicked. It updates the table of results
    as well as updates the paging controls.
*/
function PagingControlClick(event, page)
{
    UpdateLocationsTablePagingControls(currentLocations.length, page);
    UpdateLocationsTableLocations(page);
}

/*
    This function returns the selected table page of the results digest
    paging controls.
*/
function GetSelectedTablePage()
{
    var result = 0;
    var pagingContainers = $$('.' + storeLocatorPagingControlsClass);
        
    for(var i = 0; i < pagingContainers.length; i++)
    {
        var links = pagingContainers[i].select('a');
    
        for(var j = 0; j < links.length; j++)    
        {
            var link = links[j];
            
            if(link.className == 'selected')
            {
                result = link.innerHTML;
                
                break;
            }
        }
    }
    
    return result;
}

/*
    This function opens a printer friendly window of what's currently on the screen.
*/
function OpenPrinterFriendly()
{
    GetStateLink('Print', function(transport) {
        window.open(transport.responseText);
    });
}

/*
    This function synchronizes the checkboxes on the event portion of the search so that parents and
    children are properly checked (you can't have "Friday Night Magic" checked unless the "Magic: The Gathering" 
    parent is selected as well).
*/
function UpdateBrandCheck(event, control, name, values, group)
{
    var boxes = $$('input[name="' + name + '"]');
    var split = values.split(';');
    
    for(var i = 0; i < boxes.length; i++)
    {
        for(var j = 0; j < split.length; j++)
        {
            if(boxes[i].value == split[j])
            {
                if(control.checked || (!control.checked && group))
                {
                    boxes[i].checked = control.checked; 
                }
            }
        }        
    }
}

/*  
    This method will clean up all of the Google maps markers and application state
    that was used throughout the life of the page.
*/
function BodyUnload()
{
    GUnload();
    
    icons = null;
    visibleMarkers = null;
    map = null;
    geocoder = null;
    currentLocations = null;
    progressBar = null;
    tableProgressBar = null;
    latitude = null;
    longitude = null;
    lastBounds = null;
    currentDirections = null;
    linkContainer = null;
    messageContainer = null;
    lastInfoWindowID = null;
    locationIDToOpen = null;
    lastDirectionsQuery = null;
}