/*
 * jQuery Placeholder Plugin.
 * http://github.com/mudge/jquery_placeholder
 *
 * A plugin to make HTML5's placeholder attribute work in non-HTML5-supporting
 * browsers.
 *
 * Copyright (c) Paul Mucur (http://mucur.name), 2010.
 * Licensed under the MIT licence (see LICENSE.txt).
 */
(function(a){a.placeholder={className:"jquery_placeholder",supportedNatively:function(c){var b=document.createElement(c);return"placeholder" in b},backwardsCompatibility:function(){if(!a.placeholder.supportedNatively("input")&&!a.placeholder.supportedNatively("textarea")){var b=":input"}else{if(!a.placeholder.supportedNatively("textarea")){var b="textarea"}else{var b=null}}if(b){a(window).unload(function(){a(b+"."+a.placeholder.className).val("")});a(b+"[placeholder]").each(function(){var c=a(this);var d=c.attr("placeholder");if(!c.attr("defaultValue")&&c.val()==d){c.val("")}c.blur(function(){if(this!=document.activeElement&&c.val()==""){c.addClass(a.placeholder.className).val(d)}}).focus(function(){if(c.hasClass(a.placeholder.className)){c.val("").removeClass(a.placeholder.className)}}).change(function(){if(c.hasClass(a.placeholder.className)){c.removeClass(a.placeholder.className)}}).parents("form:first").submit(function(){c.triggerHandler("focus")}).end().triggerHandler("blur")})}}};a(a.placeholder.backwardsCompatibility)})(jQuery);


/**
  * Dairy Queen Store Locator
  *
  * OPTIONS:
  * 
  * mapCanvas       [string]: Selector for div that will contain the map (must be an ID)
  * searchForm      [string]: Selector for search form
  * searchSubmit    [string]: Selector for search form submit button (we use a link for asthetic reasons, so we have to explicitly add a submit handler to it)
  * searchInput     [string]: Selector for search query text input (we update it with a human-readable address after geocoding)
  * tripPlanner     [string]: Selector for div that will contain the trip planner 
  * defaultLat      [number]: Default Latitude (will be overridden if autoDetect is true)
  * defaultLng      [number]: Default Longitude (will be overridden if autoDetect is true)
  * defaultZoom     [number]: Default zoom (1-21)
  * filters: {},    [object]: Empty, created on initialization so it is accessible to all methods
  * markers: {},    [object]: Empty, created on initialization so it is accessible to all methods
  * pagination: {
  *   pages:        [string]: Selector for 1 individual page of results
  *   controls:     [string]: Selector for next AND previous controls--must include both elements
  *   scroller:     [string]: Selector for div that will contain all pages of results
  *   currentPage:  [string]: Selector for element that will contain the page number of the current page of results
  *   totalPages:   [string]: Selector for element that will contain number of total pages of results
  * }
  * ajaxUrl:        [string]: URL for web services requests, defaluts to '/stores.json'
  * 
  */
(function(global, jQuery){
/**
  * CONSTANTS:
  */
  var container, currentLocation, featureSet, filters, isIE6, locator, mapBounds, markerImagePath,
  infoWindow        = null,
  LOCALE            = window.location.pathname.split("/")[1].toLowerCase() || 'us-en',
  STORE_LINK        = '/' + LOCALE + '/store-details/',
  INFO_WINDOW_PATH  = '/templates/img/map_balloon.png',
  GMAPS_URL         = 'http://maps.google.com/maps?f=d&mra=ls&t=m&source=s_d',
  LANGUAGE_STRINGS  = {
    "us-en": {
      breakfast_flag: "Breakfast",
      burgers_flag: "Burgers",
      cakes_flag: "Cakes",
      chicken_flag: "Chicken Strips or Sandwiches",
      drive_thru_flag: "Drive Thru",
      dq_flag: "DQ Treats",
      gift_card_flag: "Gift Cards",
      grill_n_chill_flag: "Grill &amp; Chill",
      hot_dogs_flag: "Hot-dogs",
      oj_flag: "Orange Julius",
      show_on_map: "Show on Map",
      directions_link_text: "Get Directions",
      empty_search_string: "Please enter a valid location."
    },
    "ca-fr": {
      breakfast_flag: "D&eacute;jeuner",
      burgers_flag: "Hamburgers",
      cakes_flag: "G&acirc;teaux",
      chicken_flag: "Lani&eacute;res de poulet ou sandwichs",
      drive_thru_flag: "Service au volant",
      dq_flag: "Desserts DQ",
      gift_card_flag: "Cartes cadeaux",
      grill_n_chill_flag: "Grill &amp; Chill",
      hot_dogs_flag: "Hot Dogs",
      oj_flag: "Orange Julius",
      show_on_map: "Montrer sur la carte",
      directions_link_text: "Obtenir des indications",
      empty_search_string: "S&#x27;il vous pla&icirc;t entrer un emplacement valide."
    }
  };
  
  /**
    * Adding events to form elements and other user controls
    */
  function setupEvents() {
    // Find search form, attach handler
    this.searchForm = this.container.find(this.options.searchForm).submit(jQuery.proxy(doSearch, this));
    
    // Find search form submit, attach handler
    this.searchForm.find(this.options.searchSubmit).bind('click', jQuery.proxy(function() {
      if (this.searchInput.val()) this.searchForm.submit();
      return false;
    }, this));
    
    // Find search input field, select text on focus
    this.searchInput = this.searchForm.find(this.options.searchInput).bind('focus', function() {
      this.select();
    });
    
    // Find filter checkboxes
    this.filterCheckboxes = this.searchForm.find(':checkbox');
    this.filterCheckboxes.bind('click', jQuery.proxy(function(e) {
      var checked = jQuery.map(this.filterCheckboxes.filter(':checked'), function(el, i) { return el.name; });
      this.filterStores(checked);
    }, this));
    jQuery.proxy(this.createFilters(this.filterCheckboxes), this);
    
    // Find trip planner and form
    this.tripPlanner = this.container.find(this.options.tripPlanner);
    this.planTripForm = this.tripPlanner.find('form').submit(jQuery.proxy(getDirections, this));
    
    // Find trip planner controls, attach handlers
    this.tripPlanner.find('p.last a').bind('click', jQuery.proxy(function() {
      this.planTripForm.submit();
    }, this));
    this.tripPlanner.find('.trip-planner-toggle').bind('click', function() {
      jQuery(this.hash).toggle();
      return false;
    });
    
    // Add custom map controls
    this.customControls(this.gmap);
    
    // Setup "What's this?" tooltip link in search form
    (function() {
      var tt    = jQuery('#tooltip'),
          link  = jQuery('#inst-link');
      
      link.bind('mouseover mouseout', function(e) {
        tt.css({
          top: link.offset().top - tt.outerHeight(),
          left: link.offset().left
        });
        (tt.css('display') !== 'none') ? tt.hide() : tt.show();
      });
    })();
  }
  
  /**
    * Building and placing custom map controls
    * @param [object] map: GMaps Map object that will receive the controls
    */
  function customControls(map) {
    // TODO: Refactor this to be less repetitive -- Define array or object of button info
    // TODO: Add the other custom controlls -- map panning
    var defaultCss    = { position: 'relative', left: 'auto', top: 'auto', padding: '5px 0 0', backgroundPosition: '0 5px' },
        mapTypeNormal = jQuery('<div class="mapControl2 map selected"></div>').css(defaultCss).attr('title', 'Show street map'),
        mapTypeHybrid = jQuery('<div class="mapControl2 hybrid"></div>').css(defaultCss).attr('title', 'Show satellite imagery with street names'),
        mapTypeSatellite = jQuery('<div class="mapControl2 satellite"></div>').css(defaultCss).attr('title', 'Show satellite imagery'),
        zoomIn = document.createElement('div'),
        zoomOut = document.createElement('div');
        
        zoomIn.className = 'mapControl zoomIn';
        zoomIn.title = 'Zoom In';
        zoomOut.className = 'mapControl zoomOut';
        zoomOut.title = 'Zoom Out';
        
    function resetActiveControls() {
      mapTypeNormal.removeClass('selected');
      mapTypeHybrid.removeClass('selected');
      mapTypeSatellite.removeClass('selected');
    }
    
    google.maps.event.addDomListener(mapTypeNormal[0], 'click', function() {
      resetActiveControls();
      mapTypeNormal.addClass('selected');
      map.setMapTypeId('roadmap');
      this.index = 1;
    });
    google.maps.event.addDomListener(mapTypeHybrid[0], 'click', function() {
      resetActiveControls();
      mapTypeHybrid.addClass('selected');
      map.setMapTypeId('hybrid');
      this.index = 2;
    });
    google.maps.event.addDomListener(mapTypeSatellite[0], 'click', function() {
      resetActiveControls();
      mapTypeSatellite.addClass('selected');
      map.setMapTypeId('satellite');
      this.index = 3;
    });
    
    google.maps.event.addDomListener(zoomIn, 'click', function() {
      map.setZoom(map.getZoom() + 1);
    });
    google.maps.event.addDomListener(zoomOut, 'click', function() {
      map.setZoom(map.getZoom() - 1);
    });
    
    map.controls[google.maps.ControlPosition.TOP_RIGHT].push(mapTypeSatellite[0]);
    map.controls[google.maps.ControlPosition.TOP_RIGHT].push(mapTypeHybrid[0]);
    map.controls[google.maps.ControlPosition.TOP_RIGHT].push(mapTypeNormal[0]);
    
    map.controls[google.maps.ControlPosition.LEFT].push(zoomIn);
    map.controls[google.maps.ControlPosition.LEFT].push(zoomOut);
  }
  
  function createFilters(filterNames) {
    var names = [];
    
    jQuery.each(filterNames, function(i, el) {
      names.push(el.name);
    });
    
    for (var i=0; i < names.length; i++) {
      this.options.filters[names[i]] = [];
    }
  }

  function filterStores(checked) {
    var subset   = [],
        showOnly = checked,
        results  = [];

    for (var i=0; i < showOnly.length; i++) {
      jQuery.merge(subset, this.options.filters[showOnly[i]]);
    }
    
    if (this.infoBox !== undefined) this.infoBox.setMap(null);
    
    for (k in this.options.markers) {
      if (jQuery.inArray(parseInt(k), subset) > -1 || subset.length === 0) {
        this.options.markers[k].setVisible(true);
        results.push(this.options.markers[k].storeData);
      } else {
        this.options.markers[k].setVisible(false);
      }
    }
    
    this.buildResults(this.options.pagination.scroller, results, 5);
  }
  
  function doSearch() {
    // Clear directions markers and polyline if they exist
    this.directionsDisplay.setMap(null);
    // Clear previous results
    jQuery.proxy(this.resetMap(this.gmap), this);
    
    // serialize form data, geocode address field, call request locations
    var query = this.searchForm.serializeArray(),
        json = {                  // JSON object for search params
          filter: [] 
        },
        loc = null,               // Point of search as GMaps LatLng object
        addressString = '';       // Point of search as string for updating form input
    
    // If not coming from OJ site, set dq flag by default
    if (!this.options.fromOJ) json.filter.push('dairy_queen');
    
    // Building JSON object from query array of name/value pairs
    for (k in query) {
      if (query[k].value !== 'on') {
        json[query[k].name] = query[k].value;
      } else {
        json.filter.push(query[k].name);
      }
    }
    
    // getLocations custom callback
    function afterSearch(response) {
      this.gmap.setCenter(loc);                                           // Re-center map
      this.searchInput.val(addressString);                                // Insert geocoded response string into search field
      this.options.markers = {};                                          // Clear out markers object
      jQuery.proxy(this.setMarkers(this.gmap, response), this);           // Set new markers on map
      this.buildResults(this.options.pagination.scroller, response, 5);   // Insert result items into results list
    }
    
    function afterGeocode(response, status) {
      // TODO: use bounds to find and set optimal zoom after search
      // console.log(response[0].geometry.bounds)
      // console.log(response[0].geometry.bounds.getNorthEast);
      // console.log(response[0].geometry.bounds.getSouthWest);
      // Check for error
      if (status !== 'OK') {
        if (!this.searchInput.val()) {
          getLocationsError(LANGUAGE_STRINGS[LOCALE]);
        } else {
          getLocationsError();
        }
        return;
      }
      
      // Set variables based on geocoder response
      loc = currentLocation = response[0].geometry.location;
      addressString = response[0].formatted_address;

      // Request stores from our database based on geocoded query
      this.getLocations({
        data: {
          latitude: loc.lat(),
          longitude: loc.lng(),
          radius: json.radius,
          conditions: json.filter
        },
        success: jQuery.proxy(afterSearch, this)
      });
    }
    
    this.geocoder.geocode({ address: json.address }, jQuery.proxy(afterGeocode, this));
    
    return false;
  }
  
  function doCorridorSearch(response) {
    var coords = [],
        path   = response.routes[0].overview_path;
        
    jQuery.each(path, function(i, el) {
      var point = [el.lat(), el.lng()];
      coords.push(point);
    });
    
    this.getLocations({
      type: (jQuery.browser.msie) ? 'post' : 'post',     // This is the HTTP method for the trip planner
      data: { coordinates: coords },
      complete: jQuery.proxy(function() {
        this.tripPlanner.find('#tp-instruct').show();   // Show instructions
        this.tripPlanner.find('#progress').hide();      // Hide loader
      }, this)
    });
  }
  
  function getDirections(e) {
    var query = this.planTripForm.serializeArray(),
        json  = {};
    
    for (i in query) {
      if (v = query[i].value !== 'on') {
        json[query[i].name] = query[i].value;
      } else {
        json.filter.push(query[i].name);
      }
    }
    
    this.tripPlanner.find('#tp-instruct, #progress').toggle();  // Show loader
    this.directionsDisplay.setMap(this.gmap);
    this.directionsService.route({
      destination: json.to,
      origin: json.from,
      travelMode: google.maps.DirectionsTravelMode.DRIVING
    }, jQuery.proxy(function(response, status) {
      if (status == google.maps.DirectionsStatus.OK) {
        this.directionsDisplay.setDirections(response);
        this.doCorridorSearch(response);
      }
    }, this));
    
    return false;
  }
  
  /**
    * Map reset
    * @param [object] map: GMaps Map object to be reset
    */
  function resetMap(map) {
    // Remove all markers by setting their map parameter to null
    for (i in this.options.markers) {
      this.options.markers[i].setMap(null);
    }
    
    // Reset results pagination to page 1
    jQuery(this.options.pagination.scroller).css('left', 0).html('');
    
    // Remove infoBox if it exists
    if (this.infoBox !== undefined) this.infoBox.setMap(null);
  }
  
  /**
    * Gets locations from database
    *   -- all search queries are routed through this function
    *   -- all options are defined/re-defined in the Locator.options object
    */
  function getLocations(options) {
    // Clear previous results
    jQuery.proxy(this.resetMap(this.gmap), this);
    
    // Set defaults and extend with options
    var ajaxParams = jQuery.extend({
      url: this.options.ajaxUrl,
      type: 'get',
      dataType: 'jsonp',
      data: {
        latitude: currentLocation.lat(),
        longitude: currentLocation.lng(),
        radius: 25,
        conditions: [(this.options.fromOJ) ? 'orange_julius' : 'dairy_queen']   // If coming from OJ site, set oj flag by default, else set dq flag by default
      },
      success: jQuery.proxy(getLocationsSuccess, this),
      error: jQuery.proxy(getLocationsError, this)
    }, options);
    
    // Get stores from our database via ajax
    jQuery.ajax(ajaxParams);
  }
  
  /**
    * Default success handler for basic search query
    * @param [object] response: JSON object containing array of location objects
    */
  function getLocationsSuccess(response) {
    this.setMarkers(this.gmap, response);                               // Insert marker objects into map pane
    this.buildResults(this.options.pagination.scroller, response, 5);   // Insert result items into results list
  }

  /**
    * Default error handler for basic search query
    * @param [object] response: JSON object containing array of location objects
    */
  function getLocationsError(condition) {
    jQuery('#errorDialog').dialog({
      draggable: false,
      resizable: false,
      modal: true,
      width: 450,
      open: function() {
        var self = jQuery(this),
            msg  = self.find('.message');
        
        if (condition) msg.html(condition);
        self.find('.close').bind('click', function() {
          self.dialog('close');
          return false;
        });
      },
      close: function() {
        jQuery(this).dialog('destroy');
      }
    });
  }
  
  /**
    * Creates paginated search results
    * @param [string] results: Selector for div that will contain all pages of results
    * @param [object] locations: JSON object containing array of location objects
    * @param [number] resultsPerPage: Number of locations to display per page of results
    * TODO: Add resultsPerPage to Locator.options object
    */
  function buildResults(scroller, locations, perPage) {
    var list    = jQuery('<ul class="page"></ul>'),  // Each ul is a page of results
        results = jQuery(scroller),                  // Results div jquery extended
        items   = 0;
    
    results.html('');  // Clear out paginated results list
    
    for (var i=0; i < locations.length; i++) {
      var details  = parseStoreDetails(locations[i], true),   // Returns JSON object for single store parsed as HTML
          listItem = jQuery('<li class="result"></li>')
            .append(details)                                  // Create jquery extended <li> and append parsed HTML
            .attr('id', 'store-'+ locations[i].store_no);     // Add store_no as id
      
      list.append(listItem);                                  // Append this store to current page of results (<ul>)
      
      if ((++items === perPage) || (i == locations.length - 1)) {
        items = 0;
        list.appendTo(results);               // Append this ul.page of stores to the full list
        list = jQuery('<ul class="page"></ul>');   // Create new page for next batch of stores (<li>s)
      }
    }
    
    // Adding SHOW ON MAP handler for panning to store marker on map
    jQuery('.map-link').click(function() {
      var num = this.hash.replace('#', '') * 1;
      google.maps.event.trigger(locator.options.markers[num], 'click');
      locator.options.markers[num].setVisible(true);
    });
    
    // Attaching events for paging through results
    setupPagination.call(this);
  }
  
  /*
   *  Results list pagination
   */
  function setupPagination() {
    // Extend all pagination elements from their selectors in the config object
    var scroller  = jQuery(this.options.pagination.scroller),
        controls  = jQuery(this.options.pagination.controls),
        pages     = jQuery(this.options.pagination.pages),
        count     = jQuery(this.options.pagination.totalPages),
        current   = jQuery(this.options.pagination.currentPage),
        locked    = false,
        pageWidth = pages.first().width() || 1;
    
    controls.unbind('click');
    
    if (pages.length > 1) {
      pageWidth = pages.first().width();
      scroller.width(pageWidth * pages.length);
      controls.bind('click', handleClick);
      controls.eq(1).show();
    } else {
      scroller.width('auto');
      controls.hide();
    }
    scroller.css('left', 0);
    controls.eq(0).hide();
    current.html('1');
    count.html(pages.length);
    pageNum = 0;
    
    function handleClick(e) {
      var that    = jQuery(e.currentTarget),
          current = scroller.position().left,
          end     = (that.hasClass('next')) ? current - pageWidth : current + pageWidth,
          left    = Math.floor(end / pageWidth) * pageWidth;
          
      if (!locked && Math.abs(left) < scroller.width() && end < 1) {
        lock();
        scroller.animate({
          left: left
        }, 250, function() {
          unlock();
        });
      }
      return false;
    }
    function getPageNum() {
      var pageNum = Math.ceil(pages.length - ((scroller.width() + scroller.position().left) / pageWidth));
      
      if (pageNum === 0) {
        controls.eq(0).hide();
      } else if (pageNum === pages.length - 1) {
        controls.eq(1).hide();
      } else {
        controls.show();
      }
      current.html(pageNum + 1);
    }
    function lock() {
      locked = true;
      controls.show();
    }
    function unlock() {
      locked = false;
      getPageNum();
    }
  }
  
  /**
    * Plots markers on map
    * @param [object] map: GMaps Map object
    * @param [object] locations: JSON object containing array of location objects
    */
  function setMarkers(map, locations) {
    var image = new google.maps.MarkerImage(markerImagePath,
        new google.maps.Size(50,40),    // This marker is 50 pixels wide by 40 pixels tall.
        new google.maps.Point(0,0),     // The origin for this image is 0,0.
        new google.maps.Point(25,20)),  // The anchor for this image is the center of the logo at 25,20.
        
    // Shape to define the clickable region of the icon.
    shape = {
      coord: [5,20,25,5,45,20,25,35,5,20],
      type: 'poly'
    };
    
    // Loop through locations objects and create marker objects for each one
    for (var i = 0; i < locations.length; i++) {
      var store = locations[i],
          myLatLng = new google.maps.LatLng(store['geo_data']['latitude'], store['geo_data']['longitude']),
          marker = new google.maps.Marker({
            clickable: true,
            position: myLatLng,
            map: map,
            icon: image,
            shape: shape,
            title: store['address']['store_name'],
            zIndex: i+1,
            storeData: locations[i]   // Tack the store's data object onto the marker for future reference
          });
      
      this.options.markers[store.store_no] = marker;  // Build associative array so we can access marker objects by their corresponding store_no
      jQuery.proxy(this.cacheFeatureFlags(store.feature_flags, store.store_no), this);
      
      // Adding click event for markers
      google.maps.event.addListener(marker, 'click', function() {
        createInfoBox(this, this.storeData);
      });
    }
  }
  
  /**  
    * Helper function to bulid directions link
    * @param [object] store: Location object from which to build the directions link
    */
  function buildDirectionsLink(store) {
    var address = store['address'],
        destString = /*address.store_name +'+'+*/ address.street_address +'+'+ address.city +'+'+ address.state_province_abbr +'+'+ address.postal_code;  // TODO: UNCOMMENT FOR STAGING / PRODUCTION
    return GMAPS_URL +'&q='+ destString +'&daddr='+ destString +'&ll='+ store.geo_data.latitude +','+ store.geo_data.longitude;
  }
  
  /**  
    * Helper function to build features icons
    * @param [array] features: Array of features for a given store, pulled from location object
    */
  function buildFeatureIcons(features) {
    featureSet = jQuery.map(features, function(el, i) {
      var title = (LANGUAGE_STRINGS[LOCALE][el] !== undefined) ? ' alt="'+ LANGUAGE_STRINGS[LOCALE][el] +'"' : ' ';
      return '<img src="/templates/img/map_ico_'+ el +'.png" class="feature-icon" '+ title +'/>';
    });
    return featureSet.join('\n');
  }
  
  /**
    * Stores feature flags as key => value pairs for a given store number 
    * in the Locator.options.filters obejct for future reference.
    * @param [array] features: Array of features for a given store, pulled from location object
    * @param [string] store_no: Store number to use as key in filters object
    */
  function cacheFeatureFlags(features, store_no) {
    for (var i=0; i < features.length; i++) {
      var key = features[i].replace('_flag', '').toString();
      if (this.options.filters[key] !== undefined) this.options.filters[key].push(store_no);
    };
  }
  
  /**
    * Parser that transforms location object into html content for custom infoWindow (infoBox) and search results list
    * @param [object] store: Location object from which to build the html
    * @param [boolean] isListResult: Determines whether store should be parsed as infoWindow (false or undefined) or result list item (true)
    */
  function parseStoreDetails(store, isListResult) {
    var address     = store['address'],
        location    = (address['location_name']) ? '<div>'+ address['location_name'] +'</div>' : '',
        lineBreak   = (LOCALE !== 'us-en') ? '<br/>' : '',
        vcardClass  = '',
        phoneNumber = '',
        distance    = '',
        featurePane = '',
        storeHtml   = '';
    
    function handleAltType(bool) {
      if (bool) {
        vcardClass  = 'uid';
        distance    = '<div>'+ Number(store['geo_data']['dist']).toPrecision(3) +' miles from center of search</div>';
        featurePane = '<p><span class="arrow_orng_flex"><a href="#'+ store['store_no'] +'" class="map-link">'+ LANGUAGE_STRINGS[LOCALE].show_on_map +'</a></span>'+ lineBreak +'<span class="arrow_orng_flex"><a href="'+ buildDirectionsLink(store) +'">'+ LANGUAGE_STRINGS[LOCALE].directions_link_text +'</a></span></p>\n';
      } else {
        vcardClass  = 'pane';
        featurePane = '<div class="pane" style="display:none;">'+ buildFeatureIcons(store['feature_flags']) +'</div>\n';
      }
    }
    handleAltType(isListResult);
    
    if (store['phone_number']) {
      phoneNumber = '<div class="tel"><span class="value">'+ store['phone_number'] +'</span></div>\n';
    }
    storeHtml = 
      '<div class="vcard '+ vcardClass +'">'+
        '<a href="'+ STORE_LINK + store['store_id'] + '/" class="fn org url">'+ address['store_name'] +'</a>'+
        '<div class="adr"><div class="street-address">'+ address['street_address'] +'</div>\n'+ location +'<span class="locality">'+ address['city'] +', </span><span class="region">'+ address['state_province_abbr'] +'&nbsp;</span><span class="postal-code">'+ address['postal_code'] +'</span>\n'+ phoneNumber + distance +'</div>'+
      '</div>' + featurePane;
    return storeHtml;
  }
  
  /**
    * Creates custom infoWindow (infoBox from infoBox.js)
    * @param [object] marker: GMaps Marker object
    * @param [object] store: Location object
    */
  function createInfoBox(marker, store) {
    if (typeof locator.infoBox !== 'undefined') locator.infoBox.setMap(null);
    
    var destination = marker.getPosition(),
        tooltip     = jQuery('#InfoBoxTooltip'),
        storeLink   = STORE_LINK + store['store_id'] + '/';
    
    // Create and render info box passing in content parsed above
    locator.infoBox = new InfoBox({
      latlng: destination,
      map: marker.map,
      vOffset: -202,
      hOffset: -29,
      content: parseStoreDetails(store, false),
      backgroundImg: INFO_WINDOW_PATH
    });
    
    function showTooltip(img, tooltip) {
      // Don't display tooltip if alt attribute is empty
      if (!img.alt) return;
      // Get position of image, move tooltip accordingly and display
      var pos = jQuery(img).position();
      tooltip[0].innerHTML = img.alt;
      tooltip.css({
        left: Math.ceil(pos.left) + 20,
        top: Math.ceil(pos.top) - 15
      }).show();
    }
    
    // TODO: make this more efficient
    jQuery(document).bind('infoBoxAdded', function(e, div) {
      var imgs       = jQuery('.pane', div).find('img.feature-icon'),
          tooltip    = jQuery('#InfoBoxTooltip', div),
          details    = jQuery('.detailsButton', div),
          directions = jQuery('.directionsButton', div);
      
      imgs.hover(
        function(el) { showTooltip(el.currentTarget, tooltip); },
        function(el) { tooltip.hide(); }
      );
      
      details.attr('href', storeLink);
      directions.attr({
        href: buildDirectionsLink(store),
        target: '_blank'
      });
    });
  }
    
  /* Construtor */
  function Locator(container, options) {
    this.container = jQuery(container);
    
    this.options = jQuery.extend({
      mapCanvas: '#MapCanvas',
      searchForm: '#StoreLocator',
      searchSubmit: 'a.submit',
      searchInput: '#address',
      tripPlanner: '#trip-planner',
      defaultLat: 44.9799654,
      defaultLng: -93.2638361,
      defaultZoom: 11,
      autoDetect: true,
      filters: {},
      markers: {},
      pagination: {
        pages: 'ul.page',
        controls: '#resultsNext, #resultsPrevious',
        scroller: '#results',
        currentPage: '#resultsCurrentPage',
        totalPages: '#resultsTotalPage'
      },
      ajaxUrl: (jQuery.browser.msie) ? '/templates/proxy/storeLocatorAjax.cfm' : '/templates/proxy/storeLocatorAjax.cfm',
      dqMarkerImage: '/templates/img/ico_dq_sign.png',
      ojMarkerImage: '/templates/img/ico_oj_sign.png',
      ojCssPath: '/templates/css/oj_locator.css',
      fromOJ: window.location.hash === "#orange-julius"
    }, options);

    // Local variables for Map initialization
    var that       = this,
        mapCanvas  = this.container.find(this.options.mapCanvas)[0],
        mapOptions = {
          zoom: this.options.defaultZoom,
          disableDefaultUI: true,
          mapTypeId: google.maps.MapTypeId.ROADMAP
        };
    
    function initializeMap(lat, lng) {
      mapOptions.center = currentLocation = new google.maps.LatLng(lat, lng);
      that.gmap = new google.maps.Map(mapCanvas, mapOptions);
      currentLocation = that.gmap.getCenter();
      that.getLocations();
      filters = that.filters;
      setupEvents.call(that);
    }
    
    function afterLocate(position) {
      // Build map with auto-detected Lat/Lng
      initializeMap(position.coords.latitude, position.coords.longitude);
    }
    function noLocate(positionError) {
      // If user denied to share location, build with default Lat/Lng defined in options object
      if (positionError.code) initializeMap(this.options.defaultLat, this.options.defaultLng);
    }
    
    // Add OJ stylesheet if coming from oj site
    if (this.options.fromOJ) {
      jQuery("<link rel='stylesheet' type='text/css' />").appendTo('head').attr('href', this.options.ojCssPath);
      jQuery('input#orange_julius').attr('checked', 'checked');
    }
    
    // Assign correct marker image path
    markerImagePath = this.options[(this.options.fromOJ) ? 'ojMarkerImage' : 'dqMarkerImage'];
    
    // // Initializing Map and Geocoder objects
    this.geocoder = new google.maps.Geocoder();
    // 
    // // Initializing Directions-related objects
    this.directionsService = new google.maps.DirectionsService();
    this.directionsDisplay = new google.maps.DirectionsRenderer();
    
    isIE6 = jQuery.browser.msie && jQuery.browser.version.indexOf(6) != -1;
    if (isIE6) jQuery('#upgrade-message').show();

		this.container = jQuery(container);
		this.searchForm = this.container.find(this.options.searchForm);
		this.searchInput = this.searchForm.find(this.options.searchInput);

		
		if (this.searchInput.val()) {
				this.gmap     = new google.maps.Map(mapCanvas, mapOptions);			
		    setupEvents.call(this);
        this.searchForm.submit();
    } else {
        //initializeMap(this.options.defaultLat, this.options.defaultLng);
        //mapOptions.center = currentLocation = new google.maps.LatLng(this.options.defaultLat, this.options.defaultLng);
        //this.getLocations();
        initializeMap(this.options.defaultLat, this.options.defaultLng);
    }
  }
  
  // Add a jQuery plugin that will create a new instance of the locator class.
  if (typeof jQuery !== 'undefined') {
    global.jQuery.fn.locator = function(options) {
      global.locator = locator = new Locator(this, options);
      return locator;
    };
  }
  
  Locator.prototype.resetMap = resetMap;
  Locator.prototype.setMarkers = setMarkers;
  Locator.prototype.getLocations = getLocations;
  Locator.prototype.buildResults = buildResults;
  Locator.prototype.filterStores = filterStores;
  Locator.prototype.createFilters = createFilters;
  Locator.prototype.customControls = customControls;
  Locator.prototype.doCorridorSearch = doCorridorSearch;
  Locator.prototype.cacheFeatureFlags = cacheFeatureFlags;
  
  Locator.prototype.pas=function(lat,lng){var that = this;this.gmap.setZoom(3);this.gmap.setCenter(new google.maps.LatLng(39.8625,-98.577068));this.getLocations({data:{latitude: 39.8625||lat,longitude:-98.577068||lng,radius:10000},success:function(response){jQuery.proxy(that.setMarkers(that.gmap,response),that);}});}
  
})(this, jQuery);
