thirdparty/jquery-store-locator-plugin/assets/js/plugins/storeLocator/jquery.storelocator.js
/*! jQuery Google Maps Store Locator - v2.7.2 - 2016-12-03
* http://www.bjornblog.com/web/jquery-store-locator-plugin
* Copyright (c) 2016 Bjorn Holine; Licensed MIT */
;(function ($, window, document, undefined) {
'use strict';
var pluginName = 'storeLocator';
// Only allow for one instantiation of this script and make sure Google Maps API is included
if (typeof $.fn[pluginName] !== 'undefined' || typeof google === 'undefined') {
return;
}
// Variables used across multiple methods
var $this, map, listTemplate, infowindowTemplate, dataTypeRead, originalOrigin, originalData, originalZoom, dataRequest, searchInput, addressInput, olat, olng, storeNum, directionsDisplay, directionsService, prevSelectedMarkerBefore, prevSelectedMarkerAfter, firstRun;
var featuredset = [], locationset = [], normalset = [], markers = [];
var filters = {}, locationData = {}, GeoCodeCalc = {}, mappingObj = {};
// Create the defaults once. DO NOT change these settings in this file - settings should be overridden in the plugin call
var defaults = {
'mapID' : 'bh-sl-map',
'locationList' : 'bh-sl-loc-list',
'formContainer' : 'bh-sl-form-container',
'formID' : 'bh-sl-user-location',
'addressID' : 'bh-sl-address',
'regionID' : 'bh-sl-region',
'mapSettings' : {
zoom : 12,
mapTypeId: google.maps.MapTypeId.ROADMAP
},
'markerImg' : null,
'markerDim' : null,
'catMarkers' : null,
'selectedMarkerImg' : null,
'selectedMarkerImgDim' : null,
'disableAlphaMarkers' : false,
'lengthUnit' : 'm',
'storeLimit' : 26,
'distanceAlert' : 60,
'dataType' : 'xml',
'dataLocation' : 'data/locations.xml',
'dataRaw' : null,
'xmlElement' : 'marker',
'listColor1' : '#ffffff',
'listColor2' : '#eeeeee',
'originMarker' : false,
'originMarkerImg' : null,
'originMarkerDim' : null,
'bounceMarker' : true,
'slideMap' : true,
'modal' : false,
'overlay' : 'bh-sl-overlay',
'modalWindow' : 'bh-sl-modal-window',
'modalContent' : 'bh-sl-modal-content',
'closeIcon' : 'bh-sl-close-icon',
'defaultLoc' : false,
'defaultLat' : null,
'defaultLng' : null,
'autoComplete' : false,
'autoCompleteOptions' : {},
'autoGeocode' : false,
'geocodeID' : null,
'maxDistance' : false,
'maxDistanceID' : 'bh-sl-maxdistance',
'fullMapStart' : false,
'fullMapStartBlank' : false,
'fullMapStartListLimit' : false,
'noForm' : false,
'loading' : false,
'loadingContainer' : 'bh-sl-loading',
'featuredLocations' : false,
'pagination' : false,
'locationsPerPage' : 10,
'inlineDirections' : false,
'nameSearch' : false,
'searchID' : 'bh-sl-search',
'nameAttribute' : 'name',
'visibleMarkersList' : false,
'dragSearch' : false,
'infowindowTemplatePath' : 'assets/js/plugins/storeLocator/templates/infowindow-description.html',
'listTemplatePath' : 'assets/js/plugins/storeLocator/templates/location-list-description.html',
'KMLinfowindowTemplatePath': 'assets/js/plugins/storeLocator/templates/kml-infowindow-description.html',
'KMLlistTemplatePath' : 'assets/js/plugins/storeLocator/templates/kml-location-list-description.html',
'listTemplateID' : null,
'infowindowTemplateID' : null,
'taxonomyFilters' : null,
'taxonomyFiltersContainer' : 'bh-sl-filters-container',
'exclusiveFiltering' : false,
'querystringParams' : false,
'debug' : false,
'sessionStorage' : false,
'markerCluster' : null,
'infoBubble' : null,
// Callbacks
'callbackNotify' : null,
'callbackRegion' : null,
'callbackBeforeSend' : null,
'callbackSuccess' : null,
'callbackModalOpen' : null,
'callbackModalReady' : null,
'callbackModalClose' : null,
'callbackJsonp' : null,
'callbackCreateMarker' : null,
'callbackPageChange' : null,
'callbackDirectionsRequest': null,
'callbackCloseDirections' : null,
'callbackNoResults' : null,
'callbackListClick' : null,
'callbackMarkerClick' : null,
'callbackFilters' : null,
'callbackMapSet' : null,
// Language options
'addressErrorAlert' : 'Unable to find address',
'autoGeocodeErrorAlert' : 'Automatic location detection failed. Please fill in your address or zip code.',
'distanceErrorAlert' : 'Unfortunately, our closest location is more than ',
'mileLang' : 'mile',
'milesLang' : 'miles',
'kilometerLang' : 'kilometer',
'kilometersLang' : 'kilometers',
'noResultsTitle' : 'No results',
'noResultsDesc' : 'No locations were found with the given criteria. Please modify your selections or input.',
'nextPage' : 'Next »',
'prevPage' : '« Prev'
};
// Plugin constructor
function Plugin(element, options) {
$this = $(element);
this.element = element;
this.settings = $.extend({}, defaults, options);
this._defaults = defaults;
this._name = pluginName;
this.init();
}
// Avoid Plugin.prototype conflicts
$.extend(Plugin.prototype, {
/**
* Init function
*/
init: function () {
var _this = this;
this.writeDebug('init');
// Calculate geocode distance functions
if (this.settings.lengthUnit === 'km') {
// Kilometers
GeoCodeCalc.EarthRadius = 6367.0;
}
else {
// Default is miles
GeoCodeCalc.EarthRadius = 3956.0;
}
// KML is read as XML
if (this.settings.dataType === 'kml') {
dataTypeRead = 'xml';
}
else {
dataTypeRead = this.settings.dataType;
}
// Add directions panel if enabled
if(this.settings.inlineDirections === true) {
$('.' + this.settings.locationList).prepend('<div class="bh-sl-directions-panel"></div>');
}
// Save the original zoom setting so it can be retrieved if taxonomy filtering resets it
originalZoom = this.settings.mapSettings.zoom;
// Add Handlebars helper for handling URL output
Handlebars.registerHelper('niceURL', function(url) {
if(url){
return url.replace('https://', '').replace('http://', '');
}
});
// Do taxonomy filtering if set
if (this.settings.taxonomyFilters !== null) {
this.taxonomyFiltering();
}
// Add modal window divs if set
if (this.settings.modal === true) {
// Clone the filters if there are any so they can be used in the modal
if (this.settings.taxonomyFilters !== null) {
// Clone the filters
$('.' + this.settings.taxonomyFiltersContainer).clone(true, true).prependTo($this);
}
$this.wrap('<div class="' + this.settings.overlay + '"><div class="' + this.settings.modalWindow + '"><div class="' + this.settings.modalContent + '">');
$('.' + this.settings.modalWindow).prepend('<div class="' + this.settings.closeIcon + '"></div>');
$('.' + this.settings.overlay).hide();
}
// Set up Google Places autocomplete if it's set to true
if (this.settings.autoComplete === true) {
var searchInput = document.getElementById(this.settings.addressID);
var autoPlaces = new google.maps.places.Autocomplete(searchInput, this.settings.autoCompleteOptions);
// Add listener when autoComplete selection changes.
if (this.settings.autoComplete === true) {
autoPlaces.addListener('place_changed', function(e) {
_this.processForm(e);
});
}
}
// Load the templates and continue from there
this._loadTemplates();
},
/**
* Destroy
* Note: The Google map is not destroyed here because Google recommends using a single instance and reusing it (it's not really supported)
*/
destroy: function () {
this.writeDebug('destroy');
// Reset
this.reset();
var $mapDiv = $('#' + this.settings.mapID);
// Remove marker event listeners
if(markers.length) {
for(var i = 0; i <= markers.length; i++) {
google.maps.event.removeListener(markers[i]);
}
}
// Remove markup
$('.' + this.settings.locationList + ' ul').empty();
if($mapDiv.hasClass('bh-sl-map-open')) {
$mapDiv.empty().removeClass('bh-sl-map-open');
}
// Remove modal markup
if (this.settings.modal === true) {
$('. ' + this.settings.overlay).remove();
}
// Remove map style from container
$mapDiv.attr('style', '');
// Hide map container
$this.hide();
// Remove data
$.removeData($this.get(0));
// Remove namespaced events
$(document).off(pluginName);
// Unbind plugin
$this.unbind();
},
/**
* Reset function
* This method clears out all the variables and removes events. It does not reload the map.
*/
reset: function () {
this.writeDebug('reset');
locationset = [];
featuredset = [];
normalset = [];
markers = [];
firstRun = false;
$(document).off('click.'+pluginName, '.' + this.settings.locationList + ' li');
if( $('.' + this.settings.locationList + ' .bh-sl-close-directions-container').length ) {
$('.bh-sl-close-directions-container').remove();
}
if(this.settings.inlineDirections === true) {
// Remove directions panel if it's there
var $adp = $('.' + this.settings.locationList + ' .adp');
if ( $adp.length > 0 ) {
$adp.remove();
$('.' + this.settings.locationList + ' ul').fadeIn();
}
$(document).off('click', '.' + this.settings.locationList + ' li .loc-directions a');
}
if(this.settings.pagination === true) {
$(document).off('click.'+pluginName, '.bh-sl-pagination li');
}
},
/**
* Reset the form filters
*/
formFiltersReset: function () {
this.writeDebug('formFiltersReset');
if (this.settings.taxonomyFilters === null) {
return;
}
var $inputs = $('.' + this.settings.taxonomyFiltersContainer + ' input'),
$selects = $('.' + this.settings.taxonomyFiltersContainer + ' select');
if ( typeof($inputs) !== 'object') {
return;
}
// Loop over the input fields
$inputs.each(function() {
if ($(this).is('input[type="checkbox"]') || $(this).is('input[type="radio"]')) {
$(this).prop('checked',false);
}
});
// Loop over select fields
$selects.each(function() {
$(this).prop('selectedIndex',0);
});
},
/**
* Reload everything
* This method does a reset of everything and reloads the map as it would first appear.
*/
mapReload: function() {
this.writeDebug('mapReload');
this.reset();
if ( this.settings.taxonomyFilters !== null ) {
this.formFiltersReset();
this.taxonomyFiltersInit();
}
if ((olat) && (olng)) {
this.settings.mapSettings.zoom = originalZoom;
this.processForm();
}
else {
this.mapping(mappingObj);
}
},
/**
* Notifications
* Some errors use alert by default. This is overridable with the callbackNotify option
*
* @param notifyText {string} the notification message
*/
notify: function (notifyText) {
this.writeDebug('notify',notifyText);
if (this.settings.callbackNotify) {
this.settings.callbackNotify.call(this, notifyText);
}
else {
alert(notifyText);
}
},
/**
* Distance calculations
*/
geoCodeCalcToRadian: function (v) {
this.writeDebug('geoCodeCalcToRadian',v);
return v * (Math.PI / 180);
},
geoCodeCalcDiffRadian: function (v1, v2) {
this.writeDebug('geoCodeCalcDiffRadian',arguments);
return this.geoCodeCalcToRadian(v2) - this.geoCodeCalcToRadian(v1);
},
geoCodeCalcCalcDistance: function (lat1, lng1, lat2, lng2, radius) {
this.writeDebug('geoCodeCalcCalcDistance',arguments);
return radius * 2 * Math.asin(Math.min(1, Math.sqrt(( Math.pow(Math.sin((this.geoCodeCalcDiffRadian(lat1, lat2)) / 2.0), 2.0) + Math.cos(this.geoCodeCalcToRadian(lat1)) * Math.cos(this.geoCodeCalcToRadian(lat2)) * Math.pow(Math.sin((this.geoCodeCalcDiffRadian(lng1, lng2)) / 2.0), 2.0) ))));
},
/**
* Check for query string
*
* @param param {string} query string parameter to test
* @returns {string} query string value
*/
getQueryString: function(param) {
this.writeDebug('getQueryString',param);
if(param) {
param = param.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
var regex = new RegExp('[\\?&]' + param + '=([^&#]*)'),
results = regex.exec(location.search);
return (results === null) ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
}
},
/**
* Get google.maps.Map instance
*
* @returns {Object} google.maps.Map instance
*/
getMap: function() {
return this.map;
},
/**
* Load templates via Handlebars templates in /templates or inline via IDs - private
*/
_loadTemplates: function () {
this.writeDebug('_loadTemplates');
var source;
var _this = this;
var templateError = '<div class="bh-sl-error">Error: Could not load plugin templates. Check the paths and ensure they have been uploaded. Paths will be wrong if you do not run this from a web server.</div>';
// Get the KML templates
if (this.settings.dataType === 'kml' && this.settings.listTemplateID === null && this.settings.infowindowTemplateID === null) {
// Try loading the external template files
$.when(
// KML infowindows
$.get(this.settings.KMLinfowindowTemplatePath, function (template) {
source = template;
infowindowTemplate = Handlebars.compile(source);
}),
// KML locations list
$.get(this.settings.KMLlistTemplatePath, function (template) {
source = template;
listTemplate = Handlebars.compile(source);
})
).then(function () {
// Continue to the main script if templates are loaded successfully
_this.locator();
}, function () {
// KML templates not loaded
$('.' + _this.settings.formContainer).append(templateError);
throw new Error('Could not load storeLocator plugin templates');
});
}
// Handle script tag template method
else if (this.settings.listTemplateID !== null && this.settings.infowindowTemplateID !== null) {
// Infowindows
infowindowTemplate = Handlebars.compile($('#' + this.settings.infowindowTemplateID).html());
// Locations list
listTemplate = Handlebars.compile($('#' + this.settings.listTemplateID).html());
// Continue to the main script
_this.locator();
}
// Get the JSON/XML templates
else {
// Try loading the external template files
$.when(
// Infowindows
$.get(this.settings.infowindowTemplatePath, function (template) {
source = template;
infowindowTemplate = Handlebars.compile(source);
}),
// Locations list
$.get(this.settings.listTemplatePath, function (template) {
source = template;
listTemplate = Handlebars.compile(source);
})
).then(function () {
// Continue to the main script if templates are loaded successfully
_this.locator();
}, function () {
// JSON/XML templates not loaded
$('.' + _this.settings.formContainer).append(templateError);
throw new Error('Could not load storeLocator plugin templates');
});
}
},
/**
* Primary locator function runs after the templates are loaded
*/
locator: function () {
this.writeDebug('locator');
if (this.settings.slideMap === true) {
// Let's hide the map container to begin
$this.hide();
}
this._start();
this._formEventHandler();
},
/**
* Form event handler setup - private
*/
_formEventHandler: function () {
this.writeDebug('_formEventHandler');
var _this = this;
// ASP.net or regular submission?
if (this.settings.noForm === true) {
$(document).on('click.'+pluginName, '.' + this.settings.formContainer + ' button', function (e) {
_this.processForm(e);
});
$(document).on('keydown.'+pluginName, function (e) {
if (e.keyCode === 13 && $('#' + _this.settings.addressID).is(':focus')) {
_this.processForm(e);
}
});
}
else {
$(document).on('submit.'+pluginName, '#' + this.settings.formID, function (e) {
_this.processForm(e);
});
}
// Reset button trigger
if ($('.bh-sl-reset').length && $('#' + this.settings.mapID).length) {
$(document).on('click.' + pluginName, '.bh-sl-reset', function () {
_this.mapReload();
});
}
},
/**
* AJAX data request - private
*
* @param lat {number} latitude
* @param lng {number} longitude
* @param address {string} street address
* @param geocodeData {object} full Google geocode results object
* @returns {Object} deferred object
*/
_getData: function (lat, lng, address, geocodeData ) {
this.writeDebug('_getData',arguments);
var _this = this,
northEast = '',
southWest = '',
formattedAddress = '';
// Define extra geocode result info
if ( typeof geocodeData !== 'undefined' && typeof geocodeData.geometry.bounds !== 'undefined') {
formattedAddress = geocodeData.formatted_address;
northEast = JSON.stringify( geocodeData.geometry.bounds.getNorthEast() );
southWest = JSON.stringify( geocodeData.geometry.bounds.getSouthWest() );
}
// Before send callback
if (this.settings.callbackBeforeSend) {
this.settings.callbackBeforeSend.call(this, lat, lng, address, formattedAddress, northEast, southWest);
}
// Raw data
if( _this.settings.dataRaw !== null ) {
// XML
if( dataTypeRead === 'xml' ) {
return $.parseXML(_this.settings.dataRaw);
}
// JSON
else if( dataTypeRead === 'json' ) {
if (Array.isArray && Array.isArray(_this.settings.dataRaw)) {
return _this.settings.dataRaw;
}
else if (typeof _this.settings.dataRaw === 'string') {
return $.parseJSON(_this.settings.dataRaw);
}
else {
return [];
}
}
}
// Remote data
else {
var d = $.Deferred();
// Loading
if(this.settings.loading === true){
$('.' + this.settings.formContainer).append('<div class="' + this.settings.loadingContainer +'"></div>');
}
// AJAX request
$.ajax({
type : 'GET',
url : this.settings.dataLocation + (this.settings.dataType === 'jsonp' ? (this.settings.dataLocation.match(/\?/) ? '&' : '?') + 'callback=?' : ''),
// Passing the lat, lng, address, formatted address and bounds with the AJAX request so they can optionally be used by back-end languages
data: {
'origLat' : lat,
'origLng' : lng,
'origAddress': address,
'formattedAddress': formattedAddress,
'boundsNorthEast' : northEast,
'boundsSouthWest' : southWest
},
dataType : dataTypeRead,
jsonpCallback: (this.settings.dataType === 'jsonp' ? this.settings.callbackJsonp : null)
}).done(function(p) {
d.resolve(p);
// Loading remove
if(_this.settings.loading === true){
$('.' + _this.settings.formContainer + ' .' + _this.settings.loadingContainer).remove();
}
}).fail(d.reject);
return d.promise();
}
},
/**
* Checks for default location, full map, and HTML5 geolocation settings - private
*/
_start: function () {
this.writeDebug('_start');
var _this = this,
doAutoGeo = this.settings.autoGeocode,
latlng,
originAddress;
// Full map blank start
if (_this.settings.fullMapStartBlank !== false) {
var $mapDiv = $('#' + _this.settings.mapID);
$mapDiv.addClass('bh-sl-map-open');
var myOptions = _this.settings.mapSettings;
myOptions.zoom = _this.settings.fullMapStartBlank;
latlng = new google.maps.LatLng(this.settings.defaultLat, this.settings.defaultLng);
myOptions.center = latlng;
// Create the map
_this.map = new google.maps.Map(document.getElementById(_this.settings.mapID), myOptions);
// Re-center the map when the browser is re-sized
google.maps.event.addDomListener(window, 'resize', function() {
var center = _this.map.getCenter();
google.maps.event.trigger(_this.map, 'resize');
_this.map.setCenter(center);
});
// Only do this once
_this.settings.fullMapStartBlank = false;
myOptions.zoom = originalZoom;
}
else {
// If a default location is set
if (this.settings.defaultLoc === true) {
this.defaultLocation();
}
// If there is already have a value in the address bar
if ($.trim($('#' + this.settings.addressID).val()) !== ''){
_this.writeDebug('Using Address Field');
_this.processForm(null);
doAutoGeo = false; // No need for additional processing
}
// If show full map option is true
else if (this.settings.fullMapStart === true) {
if((this.settings.querystringParams === true && this.getQueryString(this.settings.addressID)) || (this.settings.querystringParams === true && this.getQueryString(this.settings.searchID)) || (this.settings.querystringParams === true && this.getQueryString(this.settings.maxDistanceID))) {
_this.writeDebug('Using Query String');
this.processForm(null);
doAutoGeo = false; // No need for additional processing
}
else {
this.mapping(null);
}
}
// HTML5 auto geolocation API option
if (this.settings.autoGeocode === true && doAutoGeo === true) {
_this.writeDebug('Auto Geo');
_this.htmlGeocode();
}
// HTML5 geolocation API button option
if (this.settings.autoGeocode !== null) {
_this.writeDebug('Button Geo');
$(document).on('click.'+pluginName, '#' + this.settings.geocodeID, function () {
_this.htmlGeocode();
});
}
}
},
/**
* Geocode function used for auto geocode setting and geocodeID button
*/
htmlGeocode: function() {
this.writeDebug('htmlGeocode',arguments);
var _this = this;
if (this.settings.sessionStorage === true && window.sessionStorage && window.sessionStorage.getItem('myGeo')){
this.writeDebug('Using Session Saved Values for GEO');
this.autoGeocodeQuery(JSON.parse(window.sessionStorage.getItem('myGeo')));
return false;
}
else if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position){
_this.writeDebug('Current Position Result');
// To not break autoGeocodeQuery then we create the obj to match the geolocation format
var pos = {
coords: {
latitude : position.coords.latitude,
longitude: position.coords.longitude,
accuracy : position.coords.accuracy
}
};
// Have to do this to get around scope issues
if (_this.settings.sessionStorage === true && window.sessionStorage) {
window.sessionStorage.setItem('myGeo',JSON.stringify(pos));
}
_this.autoGeocodeQuery(pos);
}, function(error){
_this._autoGeocodeError(error);
});
}
},
/**
* Geocode function used to geocode the origin (entered location)
*/
googleGeocode: function (thisObj) {
thisObj.writeDebug('googleGeocode',arguments);
var _this = thisObj;
var geocoder = new google.maps.Geocoder();
this.geocode = function (request, callbackFunction) {
geocoder.geocode(request, function (results, status) {
if (status === google.maps.GeocoderStatus.OK) {
var result = {};
result.latitude = results[0].geometry.location.lat();
result.longitude = results[0].geometry.location.lng();
result.geocodeResult = results[0];
callbackFunction(result);
} else {
callbackFunction(null);
throw new Error('Geocode was not successful for the following reason: ' + status);
}
});
};
},
/**
* Reverse geocode to get address for automatic options needed for directions link
*/
reverseGoogleGeocode: function (thisObj) {
thisObj.writeDebug('reverseGoogleGeocode',arguments);
var _this = thisObj;
var geocoder = new google.maps.Geocoder();
this.geocode = function (request, callbackFunction) {
geocoder.geocode(request, function (results, status) {
if (status === google.maps.GeocoderStatus.OK) {
if (results[0]) {
var result = {};
result.address = results[0].formatted_address;
callbackFunction(result);
}
} else {
callbackFunction(null);
throw new Error('Reverse geocode was not successful for the following reason: ' + status);
}
});
};
},
/**
* Rounding function used for distances
*
* @param num {number} the full number
* @param dec {number} the number of digits to show after the decimal
* @returns {number}
*/
roundNumber: function (num, dec) {
this.writeDebug('roundNumber',arguments);
return Math.round(num * Math.pow(10, dec)) / Math.pow(10, dec);
},
/**
* Checks to see if the object is empty. Using this instead of $.isEmptyObject for legacy browser support
*
* @param obj {Object} the object to check
* @returns {boolean}
*/
isEmptyObject: function (obj) {
this.writeDebug('isEmptyObject',arguments);
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
return false;
}
}
return true;
},
/**
* Checks to see if all the property values in the object are empty
*
* @param obj {Object} the object to check
* @returns {boolean}
*/
hasEmptyObjectVals: function (obj) {
this.writeDebug('hasEmptyObjectVals',arguments);
var objTest = true;
for(var key in obj) {
if(obj.hasOwnProperty(key)) {
if(obj[key] !== '' && obj[key].length !== 0) {
objTest = false;
}
}
}
return objTest;
},
/**
* Modal window close function
*/
modalClose: function () {
this.writeDebug('modalClose');
// Callback
if (this.settings.callbackModalClose) {
this.settings.callbackModalClose.call(this);
}
// Reset the filters
filters = {};
// Undo category selections
$('.' + this.settings.overlay + ' select').prop('selectedIndex', 0);
$('.' + this.settings.overlay + ' input').prop('checked', false);
// Hide the modal
$('.' + this.settings.overlay).hide();
},
/**
* Create the location variables - private
*
* @param loopcount {number} current marker id
*/
_createLocationVariables: function (loopcount) {
this.writeDebug('_createLocationVariables',arguments);
var value;
locationData = {};
for (var key in locationset[loopcount]) {
if (locationset[loopcount].hasOwnProperty(key)) {
value = locationset[loopcount][key];
if (key === 'distance') {
value = this.roundNumber(value, 2);
}
locationData[key] = value;
}
}
},
/**
* Location distance sorting function
*
* @param locationsarray {array} locationset array
*/
sortNumerically: function (locationsarray) {
this.writeDebug('sortNumerically',arguments);
locationsarray.sort(function (a, b) {
return ((a.distance < b.distance) ? -1 : ((a.distance > b.distance) ? 1 : 0));
});
},
/**
* Filter the data with Regex
*
* @param data {array} data array to check for filter values
* @param filters {Object} taxonomy filters object
* @returns {boolean}
*/
filterData: function (data, filters) {
this.writeDebug('filterData',arguments);
var filterTest = true;
for (var k in filters) {
if (filters.hasOwnProperty(k)) {
// Exclusive filtering
if(this.settings.exclusiveFiltering === true) {
var filterTests = filters[k];
var exclusiveTest = [];
for(var l = 0; l < filterTests.length; l++) {
exclusiveTest[l] = new RegExp(filterTests[l], 'i').test(data[k].replace(/[^\x00-\x7F]/g, ''));
}
if(exclusiveTest.indexOf(true) === -1) {
filterTest = false;
}
}
// Inclusive filtering
else {
if (typeof data[k] === 'undefined' || !(new RegExp(filters[k].join(''), 'i').test(data[k].replace(/[^\x00-\x7F]/g, '')))) {
filterTest = false;
}
}
}
}
if (filterTest) {
return true;
}
},
/**
* Build pagination numbers and next/prev links - private
*
* @param currentPage {number}
* @param totalPages {number}
* @returns {string}
*/
_paginationOutput: function(currentPage, totalPages) {
this.writeDebug('_paginationOutput',arguments);
currentPage = parseFloat(currentPage);
var output = '';
var nextPage = currentPage + 1;
var prevPage = currentPage - 1;
// Previous page
if( currentPage > 0 ) {
output += '<li class="bh-sl-next-prev" data-page="' + prevPage + '">' + this.settings.prevPage + '</li>';
}
// Add the numbers
for (var p = 0; p < Math.ceil(totalPages); p++) {
var n = p + 1;
if (p === currentPage) {
output += '<li class="bh-sl-current" data-page="' + p + '">' + n + '</li>';
}
else {
output += '<li data-page="' + p + '">' + n + '</li>';
}
}
// Next page
if( nextPage < totalPages ) {
output += '<li class="bh-sl-next-prev" data-page="' + nextPage + '">' + this.settings.nextPage + '</li>';
}
return output;
},
/**
* Set up the pagination pages
*
* @param currentPage {number} optional current page
*/
paginationSetup: function (currentPage) {
this.writeDebug('paginationSetup',arguments);
var pagesOutput = '';
var totalPages;
var $paginationList = $('.bh-sl-pagination-container .bh-sl-pagination');
// Total pages
if ( this.settings.storeLimit === -1 || locationset.length < this.settings.storeLimit ) {
totalPages = locationset.length / this.settings.locationsPerPage;
} else {
totalPages = this.settings.storeLimit / this.settings.locationsPerPage;
}
// Current page check
if (typeof currentPage === 'undefined') {
currentPage = 0;
}
// Initial pagination setup
if ($paginationList.length === 0) {
pagesOutput = this._paginationOutput(currentPage, totalPages);
}
// Update pagination on page change
else {
// Remove the old pagination
$paginationList.empty();
// Add the numbers
pagesOutput = this._paginationOutput(currentPage, totalPages);
}
$paginationList.append(pagesOutput);
},
/**
* Marker image setup
*
* @param markerUrl {string} path to marker image
* @param markerWidth {number} width of marker
* @param markerHeight {number} height of marker
* @returns {Object} Google Maps icon object
*/
markerImage: function (markerUrl, markerWidth, markerHeight) {
this.writeDebug('markerImage',arguments);
var markerImg;
// User defined marker dimensions
if(typeof markerWidth !== 'undefined' && typeof markerHeight !== 'undefined') {
markerImg = {
url: markerUrl,
size: new google.maps.Size(markerWidth, markerHeight),
scaledSize: new google.maps.Size(markerWidth, markerHeight)
};
}
// Default marker dimensions: 32px x 32px
else {
markerImg = {
url: markerUrl,
size: new google.maps.Size(32, 32),
scaledSize: new google.maps.Size(32, 32)
};
}
return markerImg;
},
/**
* Map marker setup
*
* @param point {Object} LatLng of current location
* @param name {string} location name
* @param address {string} location address
* @param letter {string} optional letter used for front-end identification and correlation between list and points
* @param map {Object} the Google Map
* @param category {string} location category/categories
* @returns {Object} Google Maps marker
*/
createMarker: function (point, name, address, letter, map, category) {
this.writeDebug('createMarker',arguments);
var marker, markerImg, letterMarkerImg;
var categories = [];
// Custom multi-marker image override (different markers for different categories
if(this.settings.catMarkers !== null) {
if(typeof category !== 'undefined') {
// Multiple categories
if(category.indexOf(',') !== -1) {
// Break the category variable into an array if there are multiple categories for the location
categories = category.split(',');
// With multiple categories the color will be determined by the last matched category in the data
for(var i = 0; i < categories.length; i++) {
if(categories[i] in this.settings.catMarkers) {
markerImg = this.markerImage(this.settings.catMarkers[categories[i]][0], parseInt(this.settings.catMarkers[categories[i]][1]), parseInt(this.settings.catMarkers[categories[i]][2]));
}
}
}
// Single category
else {
if(category in this.settings.catMarkers) {
markerImg = this.markerImage(this.settings.catMarkers[category][0], parseInt(this.settings.catMarkers[category][1]), parseInt(this.settings.catMarkers[category][2]));
}
}
}
}
// Custom single marker image override
if(this.settings.markerImg !== null) {
if(this.settings.markerDim === null) {
markerImg = this.markerImage(this.settings.markerImg);
}
else {
markerImg = this.markerImage(this.settings.markerImg, this.settings.markerDim.width, this.settings.markerDim.height);
}
}
// Marker setup
if (this.settings.callbackCreateMarker) {
// Marker override callback
marker = this.settings.callbackCreateMarker.call(this, map, point, letter, category);
}
else {
// Create the default markers
if (this.settings.disableAlphaMarkers === true || this.settings.storeLimit === -1 || this.settings.storeLimit > 26 || this.settings.catMarkers !== null || this.settings.markerImg !== null || (this.settings.fullMapStart === true && firstRun === true && (isNaN(this.settings.fullMapStartListLimit) || this.settings.fullMapStartListLimit > 26 || this.settings.fullMapStartListLimit === -1))) {
marker = new google.maps.Marker({
position : point,
map : map,
draggable: false,
icon: markerImg // Reverts to default marker if nothing is passed
});
}
else {
// Letter markers image
letterMarkerImg = {
url: 'https://mt.googleapis.com/vt/icon/name=icons/spotlight/spotlight-waypoint-b.png&text=' + letter + '&psize=16&font=fonts/Roboto-Regular.ttf&color=ff333333&ax=44&ay=48'
};
// Letter markers
marker = new google.maps.Marker({
position : point,
map : map,
icon : letterMarkerImg,
draggable: false
});
}
}
return marker;
},
/**
* Define the location data for the templates - private
*
* @param currentMarker {Object} Google Maps marker
* @param storeStart {number} optional first location on the current page
* @param page {number} optional current page
* @returns {Object} extended location data object
*/
_defineLocationData: function (currentMarker, storeStart, page) {
this.writeDebug('_defineLocationData',arguments);
var indicator = '';
this._createLocationVariables(currentMarker.get('id'));
var distLength;
if (locationData.distance <= 1) {
if (this.settings.lengthUnit === 'km') {
distLength = this.settings.kilometerLang;
}
else {
distLength = this.settings.mileLang;
}
}
else {
if (this.settings.lengthUnit === 'km') {
distLength = this.settings.kilometersLang;
}
else {
distLength = this.settings.milesLang;
}
}
// Set up alpha character
var markerId = currentMarker.get('id');
// Use dot markers instead of alpha if there are more than 26 locations
if (this.settings.disableAlphaMarkers === true || this.settings.storeLimit === -1 || this.settings.storeLimit > 26 || (this.settings.fullMapStart === true && firstRun === true && (isNaN(this.settings.fullMapStartListLimit) || this.settings.fullMapStartListLimit > 26 || this.settings.fullMapStartListLimit === -1))) {
indicator = markerId + 1;
}
else {
if (page > 0) {
indicator = String.fromCharCode('A'.charCodeAt(0) + (storeStart + markerId));
}
else {
indicator = String.fromCharCode('A'.charCodeAt(0) + markerId);
}
}
// Define location data
return {
location: [$.extend(locationData, {
'markerid': markerId,
'marker' : indicator,
'length' : distLength,
'origin' : originalOrigin
})]
};
},
/**
* Set up the list templates
*
* @param marker {Object} Google Maps marker
* @param storeStart {number} optional first location on the current page
* @param page {number} optional current page
*/
listSetup: function (marker, storeStart, page) {
this.writeDebug('listSetup',arguments);
// Define the location data
var locations = this._defineLocationData(marker, storeStart, page);
// Set up the list template with the location data
var listHtml = listTemplate(locations);
$('.' + this.settings.locationList + ' ul').append(listHtml);
},
/**
* Change the selected marker image
*
* @param marker {Object} Google Maps marker object
*/
changeSelectedMarker: function (marker) {
var markerImg;
// Reset the previously selected marker
if ( typeof prevSelectedMarkerAfter !== 'undefined' ) {
prevSelectedMarkerAfter.setIcon( prevSelectedMarkerBefore );
}
// Change the selected marker icon
if(this.settings.selectedMarkerImgDim === null) {
markerImg = this.markerImage(this.settings.selectedMarkerImg);
} else {
markerImg = this.markerImage(this.settings.selectedMarkerImg, this.settings.selectedMarkerImgDim.width, this.settings.selectedMarkerImgDim.height);
}
// Save the marker before switching it
prevSelectedMarkerBefore = marker.icon;
marker.setIcon( markerImg );
// Save the marker to a variable so it can be reverted when another marker is clicked
prevSelectedMarkerAfter = marker;
},
/**
* Create the infowindow
*
* @param marker {Object} Google Maps marker object
* @param location {string} indicates if the list or a map marker was clicked
* @param infowindow Google Maps InfoWindow constructor
* @param storeStart {number}
* @param page {number}
*/
createInfowindow: function (marker, location, infowindow, storeStart, page) {
this.writeDebug('createInfowindow',arguments);
var _this = this;
// Define the location data
var locations = this._defineLocationData(marker, storeStart, page);
// Set up the infowindow template with the location data
var formattedAddress = infowindowTemplate(locations);
// Opens the infowindow when list item is clicked
if (location === 'left') {
infowindow.setContent(formattedAddress);
infowindow.open(marker.get('map'), marker);
}
// Opens the infowindow when the marker is clicked
else {
google.maps.event.addListener(marker, 'click', function () {
infowindow.setContent(formattedAddress);
infowindow.open(marker.get('map'), marker);
// Focus on the list
var markerId = marker.get('id');
var $selectedLocation = $('.' + _this.settings.locationList + ' li[data-markerid=' + markerId + ']');
if ($selectedLocation.length > 0) {
// Marker click callback
if (_this.settings.callbackMarkerClick) {
_this.settings.callbackMarkerClick.call(this, marker, markerId, $selectedLocation);
}
$('.' + _this.settings.locationList + ' li').removeClass('list-focus');
$selectedLocation.addClass('list-focus');
// Scroll list to selected marker
var $container = $('.' + _this.settings.locationList);
$container.animate({
scrollTop: $selectedLocation.offset().top - $container.offset().top + $container.scrollTop()
});
}
// Custom selected marker override
if(_this.settings.selectedMarkerImg !== null) {
_this.changeSelectedMarker(marker);
}
});
}
},
/**
* HTML5 geocoding function for automatic location detection
*
* @param position {Object} coordinates
*/
autoGeocodeQuery: function (position) {
this.writeDebug('autoGeocodeQuery',arguments);
var _this = this,
distance = null,
$distanceInput = $('#' + this.settings.maxDistanceID),
originAddress;
// Query string parameters
if(this.settings.querystringParams === true) {
// Check for distance query string parameters
if(this.getQueryString(this.settings.maxDistanceID)){
distance = this.getQueryString(this.settings.maxDistanceID);
if($distanceInput.val() !== '') {
distance = $distanceInput.val();
}
}
else{
// Get the distance if set
if (this.settings.maxDistance === true) {
distance = $distanceInput.val() || '';
}
}
}
else {
// Get the distance if set
if (this.settings.maxDistance === true) {
distance = $distanceInput.val() || '';
}
}
// The address needs to be determined for the directions link
var r = new this.reverseGoogleGeocode(this);
var latlng = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
r.geocode({'latLng': latlng}, function (data) {
if (data !== null) {
originAddress = addressInput = data.address;
olat = mappingObj.lat = position.coords.latitude;
olng = mappingObj.lng = position.coords.longitude;
mappingObj.origin = originAddress;
mappingObj.distance = distance;
_this.mapping(mappingObj);
} else {
// Unable to geocode
_this.notify(_this.settings.addressErrorAlert);
}
});
},
/**
* Handle autoGeocode failure - private
*
*/
_autoGeocodeError: function () {
this.writeDebug('_autoGeocodeError');
// If automatic detection doesn't work show an error
this.notify(this.settings.autoGeocodeErrorAlert);
},
/**
* Default location method
*/
defaultLocation: function() {
this.writeDebug('defaultLocation');
var _this = this,
distance = null,
$distanceInput = $('#' + this.settings.maxDistanceID),
originAddress;
// Query string parameters
if(this.settings.querystringParams === true) {
// Check for distance query string parameters
if(this.getQueryString(this.settings.maxDistanceID)){
distance = this.getQueryString(this.settings.maxDistanceID);
if($distanceInput.val() !== '') {
distance = $distanceInput.val();
}
}
else{
// Get the distance if set
if (this.settings.maxDistance === true) {
distance = $distanceInput.val() || '';
}
}
}
else {
// Get the distance if set
if (this.settings.maxDistance === true) {
distance = $distanceInput.val() || '';
}
}
// The address needs to be determined for the directions link
var r = new this.reverseGoogleGeocode(this);
var latlng = new google.maps.LatLng(this.settings.defaultLat, this.settings.defaultLng);
r.geocode({'latLng': latlng}, function (data) {
if (data !== null) {
originAddress = addressInput = data.address;
olat = mappingObj.lat = _this.settings.defaultLat;
olng = mappingObj.lng = _this.settings.defaultLng;
mappingObj.distance = distance;
mappingObj.origin = originAddress;
_this.mapping(mappingObj);
} else {
// Unable to geocode
_this.notify(_this.settings.addressErrorAlert);
}
});
},
/**
* Change the page
*
* @param newPage {number} page to change to
*/
paginationChange: function (newPage) {
this.writeDebug('paginationChange',arguments);
// Page change callback
if (this.settings.callbackPageChange) {
this.settings.callbackPageChange.call(this, newPage);
}
mappingObj.page = newPage;
this.mapping(mappingObj);
},
/**
* Get the address by marker ID
*
* @param markerID {number} location ID
* @returns {string} formatted address
*/
getAddressByMarker: function(markerID) {
this.writeDebug('getAddressByMarker',arguments);
var formattedAddress = "";
// Set up formatted address
if(locationset[markerID].address){ formattedAddress += locationset[markerID].address + ' '; }
if(locationset[markerID].address2){ formattedAddress += locationset[markerID].address2 + ' '; }
if(locationset[markerID].city){ formattedAddress += locationset[markerID].city + ', '; }
if(locationset[markerID].state){ formattedAddress += locationset[markerID].state + ' '; }
if(locationset[markerID].postal){ formattedAddress += locationset[markerID].postal + ' '; }
if(locationset[markerID].country){ formattedAddress += locationset[markerID].country + ' '; }
return formattedAddress;
},
/**
* Clear the markers from the map
*/
clearMarkers: function() {
this.writeDebug('clearMarkers');
var locationsLimit = null;
if (locationset.length < this.settings.storeLimit) {
locationsLimit = locationset.length;
}
else {
locationsLimit = this.settings.storeLimit;
}
for (var i = 0; i < locationsLimit; i++) {
markers[i].setMap(null);
}
},
/**
* Handle inline direction requests
*
* @param origin {string} origin address
* @param locID {number} location ID
* @param map {Object} Google Map
*/
directionsRequest: function(origin, locID, map) {
this.writeDebug('directionsRequest',arguments);
// Directions request callback
if (this.settings.callbackDirectionsRequest) {
this.settings.callbackDirectionsRequest.call(this, origin, locID, map);
}
var destination = this.getAddressByMarker(locID);
if(destination) {
// Hide the location list
$('.' + this.settings.locationList + ' ul').hide();
// Remove the markers
this.clearMarkers();
// Clear the previous directions request
if(directionsDisplay !== null && typeof directionsDisplay !== 'undefined') {
directionsDisplay.setMap(null);
directionsDisplay = null;
}
directionsDisplay = new google.maps.DirectionsRenderer();
directionsService = new google.maps.DirectionsService();
// Directions request
directionsDisplay.setMap(map);
directionsDisplay.setPanel($('.bh-sl-directions-panel').get(0));
var request = {
origin: origin,
destination: destination,
travelMode: google.maps.TravelMode.DRIVING
};
directionsService.route(request, function(response, status) {
if (status === google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
}
});
$('.' + this.settings.locationList).prepend('<div class="bh-sl-close-directions-container"><div class="' + this.settings.closeIcon + '"></div></div>');
}
$(document).off('click', '.' + this.settings.locationList + ' li .loc-directions a');
},
/**
* Close the directions panel and reset the map with the original locationset and zoom
*/
closeDirections: function() {
this.writeDebug('closeDirections');
// Close directions callback
if (this.settings.callbackCloseDirections) {
this.settings.callbackCloseDirections.call(this);
}
// Remove the close icon, remove the directions, add the list back
this.reset();
if ((olat) && (olng)) {
if (this.countFilters() === 0) {
this.settings.mapSettings.zoom = originalZoom;
}
else {
this.settings.mapSettings.zoom = 0;
}
this.processForm(null);
}
$(document).off('click.'+pluginName, '.' + this.settings.locationList + ' .bh-sl-close-icon');
},
/**
* Process the form values and/or query string
*
* @param e {Object} event
*/
processForm: function (e) {
this.writeDebug('processForm',arguments);
var _this = this,
distance = null,
$addressInput = $('#' + this.settings.addressID),
$searchInput = $('#' + this.settings.searchID),
$distanceInput = $('#' + this.settings.maxDistanceID),
region = '';
// Stop the form submission
if(typeof e !== 'undefined' && e !== null) {
e.preventDefault();
}
// Query string parameters
if(this.settings.querystringParams === true) {
// Check for query string parameters
if(this.getQueryString(this.settings.addressID) || this.getQueryString(this.settings.searchID) || this.getQueryString(this.settings.maxDistanceID)){
addressInput = this.getQueryString(this.settings.addressID);
searchInput = this.getQueryString(this.settings.searchID);
distance = this.getQueryString(this.settings.maxDistanceID);
// The form should override the query string parameters
if($addressInput.val() !== '') {
addressInput = $addressInput.val();
}
if($searchInput.val() !== '') {
searchInput = $searchInput.val();
}
if($distanceInput.val() !== '') {
distance = $distanceInput.val();
}
}
else{
// Get the user input and use it
addressInput = $addressInput.val() || '';
searchInput = $searchInput.val() || '';
// Get the distance if set
if (this.settings.maxDistance === true) {
distance = $distanceInput.val() || '';
}
}
}
else {
// Get the user input and use it
addressInput = $addressInput.val() || '';
searchInput = $searchInput.val() || '';
// Get the distance if set
if (this.settings.maxDistance === true) {
distance = $distanceInput.val() || '';
}
}
// Region
if (this.settings.callbackRegion) {
// Region override callback
region = this.settings.callbackRegion.call(this, addressInput, searchInput, distance);
} else {
// Region setting
region = $('#' + this.settings.regionID).val();
}
if (addressInput === '' && searchInput === '') {
this._start();
}
else if(addressInput !== '') {
// Geocode the origin if needed
if(typeof originalOrigin !== 'undefined' && typeof olat !== 'undefined' && typeof olng !== 'undefined' && (addressInput === originalOrigin)) {
// Run the mapping function
mappingObj.lat = olat;
mappingObj.lng = olng;
mappingObj.origin = addressInput;
mappingObj.name = searchInput;
mappingObj.distance = distance;
_this.mapping(mappingObj);
}
else {
var g = new this.googleGeocode(this);
g.geocode({'address': addressInput, 'region': region}, function (data) {
if (data !== null) {
olat = data.latitude;
olng = data.longitude;
// Run the mapping function
mappingObj.lat = olat;
mappingObj.lng = olng;
mappingObj.origin = addressInput;
mappingObj.name = searchInput;
mappingObj.distance = distance;
mappingObj.geocodeResult = data.geocodeResult;
_this.mapping(mappingObj);
} else {
// Unable to geocode
_this.notify(_this.settings.addressErrorAlert);
}
});
}
}
else if(searchInput !== '') {
// Check for existing origin and remove if address input is blank.
if ( addressInput === '' ) {
delete mappingObj.origin;
}
mappingObj.name = searchInput;
_this.mapping(mappingObj);
}
},
/**
* Checks distance of each location and sets up the locationset array
*
* @param data {Object} location data object
* @param lat {number} origin latitude
* @param lng {number} origin longitude
* @param origin {string} origin address
* @param maxDistance {number} maximum distance if set
*/
locationsSetup: function (data, lat, lng, origin, maxDistance) {
this.writeDebug('locationsSetup',arguments);
if (typeof origin !== 'undefined') {
if (!data.distance) {
data.distance = this.geoCodeCalcCalcDistance(lat, lng, data.lat, data.lng, GeoCodeCalc.EarthRadius);
}
}
// Create the array
if (this.settings.maxDistance === true && typeof maxDistance !== 'undefined' && maxDistance !== null) {
if (data.distance <= maxDistance) {
locationset.push( data );
}
else {
return;
}
}
else if(this.settings.maxDistance === true && this.settings.querystringParams === true && typeof maxDistance !== 'undefined' && maxDistance !== null) {
if (data.distance <= maxDistance) {
locationset.push( data );
}
else {
return;
}
}
else {
locationset.push( data );
}
},
/**
* Count the selected filters
*
* @returns {number}
*/
countFilters: function () {
this.writeDebug('countFilters');
var filterCount = 0;
if (!this.isEmptyObject(filters)) {
for (var key in filters) {
if (filters.hasOwnProperty(key)) {
filterCount += filters[key].length;
}
}
}
return filterCount;
},
/**
* Find the existing checked boxes for each checkbox filter - private
*
* @param key {string} object key
*/
_existingCheckedFilters: function(key) {
this.writeDebug('_existingCheckedFilters',arguments);
$('#' + this.settings.taxonomyFilters[key] + ' input[type=checkbox]').each(function () {
if ($(this).prop('checked')) {
var filterVal = $(this).val();
// Only add the taxonomy id if it doesn't already exist
if (typeof filterVal !== 'undefined' && filterVal !== '' && filters[key].indexOf(filterVal) === -1) {
filters[key].push(filterVal);
}
}
});
},
/**
* Find the existing selected value for each select filter - private
*
* @param key {string} object key
*/
_existingSelectedFilters: function(key) {
this.writeDebug('_existingSelectedFilters',arguments);
$('#' + this.settings.taxonomyFilters[key] + ' select').each(function () {
var filterVal = $(this).val();
// Only add the taxonomy id if it doesn't already exist
if (typeof filterVal !== 'undefined' && filterVal !== '' && filters[key].indexOf(filterVal) === -1) {
filters[key] = [filterVal];
}
});
},
/**
* Find the existing selected value for each radio button filter - private
*
* @param key {string} object key
*/
_existingRadioFilters: function(key) {
this.writeDebug('_existingRadioFilters',arguments);
$('#' + this.settings.taxonomyFilters[key] + ' input[type=radio]').each(function () {
if ($(this).prop('checked')) {
var filterVal = $(this).val();
// Only add the taxonomy id if it doesn't already exist
if (typeof filterVal !== 'undefined' && filterVal !== '' && filters[key].indexOf(filterVal) === -1) {
filters[key] = [filterVal];
}
}
});
},
/**
* Check for existing filter selections
*/
checkFilters: function () {
this.writeDebug('checkFilters');
for(var key in this.settings.taxonomyFilters) {
if(this.settings.taxonomyFilters.hasOwnProperty(key)) {
// Find the existing checked boxes for each checkbox filter
this._existingCheckedFilters(key);
// Find the existing selected value for each select filter
this._existingSelectedFilters(key);
// Find the existing value for each radio button filter
this._existingRadioFilters(key);
}
}
},
/**
* Check query string parameters for filter values.
*/
checkQueryStringFilters: function () {
this.writeDebug('checkQueryStringFilters',arguments);
// Loop through the filters.
for(var key in filters) {
if(filters.hasOwnProperty(key)) {
var filterVal = this.getQueryString(key);
// Only add the taxonomy id if it doesn't already exist
if (typeof filterVal !== 'undefined' && filterVal !== '' && filters[key].indexOf(filterVal) === -1) {
filters[key] = [filterVal];
}
}
}
},
/**
* Get the filter key from the taxonomyFilter setting
*
* @param filterContainer {string} ID of the changed filter's container
*/
getFilterKey: function (filterContainer) {
this.writeDebug('getFilterKey',arguments);
for (var key in this.settings.taxonomyFilters) {
if (this.settings.taxonomyFilters.hasOwnProperty(key)) {
for (var i = 0; i < this.settings.taxonomyFilters[key].length; i++) {
if (this.settings.taxonomyFilters[key] === filterContainer) {
return key;
}
}
}
}
},
/**
* Initialize or reset the filters object to its original state
*/
taxonomyFiltersInit: function () {
this.writeDebug('taxonomyFiltersInit');
// Set up the filters
for(var key in this.settings.taxonomyFilters) {
if(this.settings.taxonomyFilters.hasOwnProperty(key)) {
filters[key] = [];
}
}
},
/**
* Taxonomy filtering
*/
taxonomyFiltering: function() {
this.writeDebug('taxonomyFiltering');
var _this = this;
// Set up the filters
_this.taxonomyFiltersInit();
// Check query string for taxonomy parameter keys.
_this.checkQueryStringFilters();
// Handle filter updates
$('.' + this.settings.taxonomyFiltersContainer).on('change.'+pluginName, 'input, select', function (e) {
e.stopPropagation();
var filterVal, filterContainer, filterKey;
// Handle checkbox filters
if ($(this).is('input[type="checkbox"]')) {
// First check for existing selections
_this.checkFilters();
filterVal = $(this).val();
filterContainer = $(this).closest('.bh-sl-filters').attr('id');
filterKey = _this.getFilterKey(filterContainer);
if (filterKey) {
// Add or remove filters based on checkbox values
if ($(this).prop('checked')) {
// Add ids to the filter arrays as they are checked
if(filters[filterKey].indexOf(filterVal) === -1) {
filters[filterKey].push(filterVal);
}
if ($('#' + _this.settings.mapID).hasClass('bh-sl-map-open') === true) {
if ((olat) && (olng)) {
_this.settings.mapSettings.zoom = 0;
_this.processForm();
}
else {
_this.mapping(mappingObj);
}
}
}
else {
// Remove ids from the filter arrays as they are unchecked
var filterIndex = filters[filterKey].indexOf(filterVal);
if (filterIndex > -1) {
filters[filterKey].splice(filterIndex, 1);
if ($('#' + _this.settings.mapID).hasClass('bh-sl-map-open') === true) {
if ((olat) && (olng)) {
if (_this.countFilters() === 0) {
_this.settings.mapSettings.zoom = originalZoom;
}
else {
_this.settings.mapSettings.zoom = 0;
}
_this.processForm();
}
else {
_this.mapping(mappingObj);
}
}
}
}
}
}
// Handle select or radio filters
else if ($(this).is('select') || $(this).is('input[type="radio"]')) {
// First check for existing selections
_this.checkFilters();
filterVal = $(this).val();
filterContainer = $(this).closest('.bh-sl-filters').attr('id');
filterKey = _this.getFilterKey(filterContainer);
// Check for blank filter on select since default val could be empty
if (filterVal) {
if (filterKey) {
filters[filterKey] = [filterVal];
if ($('#' + _this.settings.mapID).hasClass('bh-sl-map-open') === true) {
if ((olat) && (olng)) {
_this.settings.mapSettings.zoom = 0;
_this.processForm();
}
else {
_this.mapping(mappingObj);
}
}
}
}
// Reset if the default option is selected
else {
if (filterKey) {
filters[filterKey] = [];
}
_this.reset();
if ((olat) && (olng)) {
_this.settings.mapSettings.zoom = originalZoom;
_this.processForm();
}
else {
_this.mapping(mappingObj);
}
}
}
});
},
/**
* Updates the location list to reflect the markers that are displayed on the map
*
* @param markers {Object} Map markers
* @param map {Object} Google map
*/
checkVisibleMarkers: function(markers, map) {
this.writeDebug('checkVisibleMarkers',arguments);
var _this = this;
var locations, listHtml;
// Empty the location list
$('.' + this.settings.locationList + ' ul').empty();
// Set up the new list
$(markers).each(function(x, marker){
if(map.getBounds().contains(marker.getPosition())) {
// Define the location data
_this.listSetup(marker, 0, 0);
// Set up the list template with the location data
listHtml = listTemplate(locations);
$('.' + _this.settings.locationList + ' ul').append(listHtml);
}
});
// Re-add the list background colors
$('.' + this.settings.locationList + ' ul li:even').css('background', this.settings.listColor1);
$('.' + this.settings.locationList + ' ul li:odd').css('background', this.settings.listColor2);
},
/**
* Performs a new search when the map is dragged to a new position
*
* @param map {Object} Google map
*/
dragSearch: function(map) {
this.writeDebug('dragSearch',arguments);
var newCenter = map.getCenter(),
newCenterCoords,
_this = this;
// Save the new zoom setting
this.settings.mapSettings.zoom = map.getZoom();
olat = mappingObj.lat = newCenter.lat();
olng = mappingObj.lng = newCenter.lng();
// Determine the new origin addresss
var newAddress = new this.reverseGoogleGeocode(this);
newCenterCoords = new google.maps.LatLng(mappingObj.lat, mappingObj.lng);
newAddress.geocode({'latLng': newCenterCoords}, function (data) {
if (data !== null) {
mappingObj.origin = addressInput = data.address;
_this.mapping(mappingObj);
} else {
// Unable to geocode
_this.notify(_this.settings.addressErrorAlert);
}
});
},
/**
* Handle no results
*/
emptyResult: function() {
this.writeDebug('emptyResult',arguments);
var center,
locList = $('.' + this.settings.locationList + ' ul'),
myOptions = this.settings.mapSettings,
noResults;
// Create the map
this.map = new google.maps.Map(document.getElementById(this.settings.mapID), myOptions);
// Callback
if (this.settings.callbackNoResults) {
this.settings.callbackNoResults.call(this, this.map, myOptions);
}
// Empty the location list
locList.empty();
// Append the no results message
noResults = $('<li><div class="bh-sl-noresults-title">' + this.settings.noResultsTitle + '</div><br><div class="bh-sl-noresults-desc">' + this.settings.noResultsDesc + '</li>').hide().fadeIn();
locList.append(noResults);
// Center on the original origin or 0,0 if not available
if ((olat) && (olng)) {
center = new google.maps.LatLng(olat, olng);
} else {
center = new google.maps.LatLng(0, 0);
}
this.map.setCenter(center);
if (originalZoom) {
this.map.setZoom(originalZoom);
}
},
/**
* Origin marker setup
*
* @param map {Object} Google map
* @param origin {string} Origin address
* @param originPoint {Object} LatLng of origin point
*/
originMarker: function(map, origin, originPoint) {
this.writeDebug('originMarker',arguments);
if (this.settings.originMarker !== true) {
return;
}
var marker,
originImg = '';
if (typeof origin !== 'undefined') {
if(this.settings.originMarkerImg !== null) {
if(this.settings.originMarkerDim === null) {
originImg = this.markerImage(this.settings.originMarkerImg);
}
else {
originImg = this.markerImage(this.settings.originMarkerImg, this.settings.originMarkerDim.width, this.settings.originMarkerDim.height);
}
}
else {
originImg = {
url: 'https://mt.googleapis.com/vt/icon/name=icons/spotlight/spotlight-waypoint-a.png'
};
}
marker = new google.maps.Marker({
position : originPoint,
map : map,
icon : originImg,
draggable: false
});
}
},
/**
* Modal window setup
*/
modalWindow: function() {
this.writeDebug('modalWindow');
if (this.settings.modal !== true) {
return;
}
var _this = this;
// Callback
if (_this.settings.callbackModalOpen) {
_this.settings.callbackModalOpen.call(this);
}
// Pop up the modal window
$('.' + _this.settings.overlay).fadeIn();
// Close modal when close icon is clicked and when background overlay is clicked
$(document).on('click.'+pluginName, '.' + _this.settings.closeIcon + ', .' + _this.settings.overlay, function () {
_this.modalClose();
});
// Prevent clicks within the modal window from closing the entire thing
$(document).on('click.'+pluginName, '.' + _this.settings.modalWindow, function (e) {
e.stopPropagation();
});
// Close modal when escape key is pressed
$(document).on('keyup.'+pluginName, function (e) {
if (e.keyCode === 27) {
_this.modalClose();
}
});
},
/**
* Handle clicks from the location list
*
* @param map {Object} Google map
* @param infowindow
* @param storeStart
* @param page
*/
listClick: function(map, infowindow, storeStart, page) {
this.writeDebug('listClick',arguments);
var _this = this;
$(document).on('click.' + pluginName, '.' + _this.settings.locationList + ' li', function () {
var markerId = $(this).data('markerid');
var selectedMarker = markers[markerId];
// List click callback
if (_this.settings.callbackListClick) {
_this.settings.callbackListClick.call(this, markerId, selectedMarker);
}
map.panTo(selectedMarker.getPosition());
var listLoc = 'left';
if (_this.settings.bounceMarker === true) {
selectedMarker.setAnimation(google.maps.Animation.BOUNCE);
setTimeout(function () {
selectedMarker.setAnimation(null);
_this.createInfowindow(selectedMarker, listLoc, infowindow, storeStart, page);
}, 700
);
}
else {
_this.createInfowindow(selectedMarker, listLoc, infowindow, storeStart, page);
}
// Custom selected marker override
if (_this.settings.selectedMarkerImg !== null) {
_this.changeSelectedMarker(selectedMarker);
}
// Focus on the list
$('.' + _this.settings.locationList + ' li').removeClass('list-focus');
$('.' + _this.settings.locationList + ' li[data-markerid=' + markerId + ']').addClass('list-focus');
});
// Prevent bubbling from list content links
$(document).on('click.'+pluginName, '.' + _this.settings.locationList + ' li a', function(e) {
e.stopPropagation();
});
},
/**
* Output total results count if HTML element with .bh-sl-total-results class exists
*
* @param locCount
*/
resultsTotalCount: function(locCount) {
this.writeDebug('resultsTotalCount',arguments);
var $resultsContainer = $('.bh-sl-total-results');
if (typeof locCount === 'undefined' || locCount <= 0 || $resultsContainer.length === 0) {
return;
}
$resultsContainer.text(locCount);
},
/**
* Inline directions setup
*
* @param map {Object} Google map
* @param origin {string} Origin address
*/
inlineDirections: function(map, origin) {
this.writeDebug('inlineDirections',arguments);
if(this.settings.inlineDirections !== true || typeof origin === 'undefined') {
return;
}
var _this = this;
// Open directions
$(document).on('click.'+pluginName, '.' + _this.settings.locationList + ' li .loc-directions a', function (e) {
e.preventDefault();
var locID = $(this).closest('li').attr('data-markerid');
_this.directionsRequest(origin, locID, map);
// Close directions
$(document).on('click.'+pluginName, '.' + _this.settings.locationList + ' .bh-sl-close-icon', function () {
_this.closeDirections();
});
});
},
/**
* Visible markers list setup
*
* @param map {Object} Google map
* @param markers {Object} Map markers
*/
visibleMarkersList: function(map, markers) {
this.writeDebug('visibleMarkersList',arguments);
if(this.settings.visibleMarkersList !== true) {
return;
}
var _this = this;
// Add event listener to filter the list when the map is fully loaded
google.maps.event.addListenerOnce(map, 'idle', function(){
_this.checkVisibleMarkers(markers, map);
});
// Add event listener for center change
google.maps.event.addListener(map, 'center_changed', function() {
_this.checkVisibleMarkers(markers, map);
});
// Add event listener for zoom change
google.maps.event.addListener(map, 'zoom_changed', function() {
_this.checkVisibleMarkers(markers, map);
});
},
/**
* The primary mapping function that runs everything
*
* @param mappingObject {Object} all the potential mapping properties - latitude, longitude, origin, name, max distance, page
*/
mapping: function (mappingObject) {
this.writeDebug('mapping',arguments);
var _this = this;
var orig_lat, orig_lng, geocodeData, origin, originPoint, page;
if (!this.isEmptyObject(mappingObject)) {
orig_lat = mappingObject.lat;
orig_lng = mappingObject.lng;
geocodeData = mappingObject.geocodeResult;
origin = mappingObject.origin;
page = mappingObject.page;
}
// Set the initial page to zero if not set
if ( _this.settings.pagination === true ) {
if (typeof page === 'undefined' || originalOrigin !== addressInput ) {
page = 0;
}
}
// Data request
if (typeof origin === 'undefined' && this.settings.nameSearch === true) {
dataRequest = _this._getData();
}
else {
// Setup the origin point
originPoint = new google.maps.LatLng(orig_lat, orig_lng);
// If the origin hasn't changed use the existing data so we aren't making unneeded AJAX requests
if((typeof originalOrigin !== 'undefined') && (origin === originalOrigin) && (typeof originalData !== 'undefined')) {
origin = originalOrigin;
dataRequest = originalData;
}
else {
// Do the data request - doing this in mapping so the lat/lng and address can be passed over and used if needed
dataRequest = _this._getData(olat, olng, origin, geocodeData);
}
}
// Check filters here to handle selected filtering after page reload
if(_this.settings.taxonomyFilters !== null && _this.hasEmptyObjectVals(filters)) {
_this.checkFilters();
}
/**
* Process the location data
*/
// Raw data
if( _this.settings.dataRaw !== null ) {
_this.processData(mappingObject, originPoint, dataRequest, page);
}
// Remote data
else {
dataRequest.done(function (data) {
_this.processData(mappingObject, originPoint, data, page);
});
}
},
/**
* Processes the location data
*
* @param mappingObject {Object} all the potential mapping properties - latitude, longitude, origin, name, max distance, page
* @param originPoint {Object} LatLng of origin point
* @param data {Object} location data
* @param page {number} current page number
*/
processData: function (mappingObject, originPoint, data, page) {
this.writeDebug('processData',arguments);
var _this = this;
var i = 0;
var orig_lat, orig_lng, origin, name, maxDistance, marker, bounds, storeStart, storeNumToShow, myOptions, distError, openMap, infowindow;
var taxFilters = {};
if (!this.isEmptyObject(mappingObject)) {
orig_lat = mappingObject.lat;
orig_lng = mappingObject.lng;
origin = mappingObject.origin;
name = mappingObject.name;
maxDistance = mappingObject.distance;
}
var $mapDiv = $('#' + _this.settings.mapID);
// Get the length unit
var distUnit = (_this.settings.lengthUnit === 'km') ? _this.settings.kilometersLang : _this.settings.milesLang;
// Save data and origin separately so we can potentially avoid multiple AJAX requests
originalData = dataRequest;
if ( typeof origin !== 'undefined' ) {
originalOrigin = origin;
}
// Callback
if (_this.settings.callbackSuccess) {
_this.settings.callbackSuccess.call(this);
}
openMap = $mapDiv.hasClass('bh-sl-map-open');
// Set a variable for fullMapStart so we can detect the first run
if (
( _this.settings.fullMapStart === true && openMap === false ) ||
( _this.settings.autoGeocode === true && openMap === false ) ||
( _this.settings.defaultLoc === true && openMap === false )
) {
firstRun = true;
}
else {
_this.reset();
}
$mapDiv.addClass('bh-sl-map-open');
// Process the location data depending on the data format type
if (_this.settings.dataType === 'json' || _this.settings.dataType === 'jsonp') {
// Process JSON
for(var x = 0; i < data.length; x++){
var obj = data[x];
var locationData = {};
// Parse each data variable
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
locationData[key] = obj[key];
}
}
_this.locationsSetup(locationData, orig_lat, orig_lng, origin, maxDistance);
i++;
}
}
else if (_this.settings.dataType === 'kml') {
// Process KML
$(data).find('Placemark').each(function () {
var locationData = {
'name' : $(this).find('name').text(),
'lat' : $(this).find('coordinates').text().split(',')[1],
'lng' : $(this).find('coordinates').text().split(',')[0],
'description': $(this).find('description').text()
};
_this.locationsSetup(locationData, orig_lat, orig_lng, origin, maxDistance);
i++;
});
}
else {
// Process XML
$(data).find(_this.settings.xmlElement).each(function () {
var locationData = {};
for (var key in this.attributes) {
if (this.attributes.hasOwnProperty(key)) {
locationData[this.attributes[key].name] = this.attributes[key].value;
}
}
_this.locationsSetup(locationData, orig_lat, orig_lng, origin, maxDistance);
i++;
});
}
// Name search - using taxonomy filter to handle
if (_this.settings.nameSearch === true) {
if(typeof searchInput !== 'undefined') {
filters[_this.settings.nameAttribute] = [searchInput];
}
}
// Taxonomy filtering setup
if (_this.settings.taxonomyFilters !== null || _this.settings.nameSearch === true) {
for(var k in filters) {
if (filters.hasOwnProperty(k) && filters[k].length > 0) {
// Let's use regex
for (var z = 0; z < filters[k].length; z++) {
// Creating a new object so we don't mess up the original filters
if (!taxFilters[k]) {
taxFilters[k] = [];
}
taxFilters[k][z] = '(?=.*\\b' + filters[k][z].replace(/([^\x00-\x7F]|[.*+?^=!:${}()|\[\]\/\\])/g, '') + '\\b)';
}
}
}
// Filter the data
if (!_this.isEmptyObject(taxFilters)) {
locationset = $.grep(locationset, function (val) {
return _this.filterData(val, taxFilters);
});
}
}
// Sort the multi-dimensional array by distance
if (typeof origin !== 'undefined') {
_this.sortNumerically(locationset);
}
// Featured locations filtering
if (_this.settings.featuredLocations === true) {
// Create array for featured locations
featuredset = $.grep(locationset, function (val) {
return val.featured === 'true';
});
// Create array for normal locations
normalset = $.grep(locationset, function (val) {
return val.featured !== 'true';
});
// Combine the arrays
locationset = [];
locationset = featuredset.concat(normalset);
}
// Check the closest marker
if (_this.isEmptyObject(taxFilters)) {
if (_this.settings.maxDistance === true && maxDistance) {
if (typeof locationset[0] === 'undefined' || locationset[0].distance > maxDistance) {
_this.notify(_this.settings.distanceErrorAlert + maxDistance + ' ' + distUnit);
}
}
else {
if (typeof locationset[0] !== 'undefined') {
if (_this.settings.distanceAlert !== -1 && locationset[0].distance > _this.settings.distanceAlert) {
_this.notify(_this.settings.distanceErrorAlert + _this.settings.distanceAlert + ' ' + distUnit);
distError = true;
}
}
else {
throw new Error('No locations found. Please check the dataLocation setting and path.');
}
}
}
// Slide in the map container
if (_this.settings.slideMap === true) {
$this.slideDown();
}
// Handle no results
if (_this.isEmptyObject(locationset) || locationset[0].result === 'none') {
_this.emptyResult();
return;
}
// Output page numbers if pagination setting is true
if (_this.settings.pagination === true) {
_this.paginationSetup(page);
}
// Set up the modal window
_this.modalWindow();
// Avoid error if number of locations is less than the default of 26
if (_this.settings.storeLimit === -1 || locationset.length < _this.settings.storeLimit || (this.settings.fullMapStart === true && firstRun === true && (isNaN(this.settings.fullMapStartListLimit) || this.settings.fullMapStartListLimit > 26 || this.settings.fullMapStartListLimit === -1))) {
storeNum = locationset.length;
}
else {
storeNum = _this.settings.storeLimit;
}
// If pagination is on, change the store limit to the setting and slice the locationset array
if (_this.settings.pagination === true) {
storeNumToShow = _this.settings.locationsPerPage;
storeStart = page * _this.settings.locationsPerPage;
if( (storeStart + storeNumToShow) > locationset.length ) {
storeNumToShow = _this.settings.locationsPerPage - ((storeStart + storeNumToShow) - locationset.length);
}
locationset = locationset.slice(storeStart, storeStart + storeNumToShow);
storeNum = locationset.length;
}
else {
storeNumToShow = storeNum;
storeStart = 0;
}
// Output location results count
_this.resultsTotalCount(locationset.length);
// Google maps settings
if ((_this.settings.fullMapStart === true && firstRun === true) || (_this.settings.mapSettings.zoom === 0) || (typeof origin === 'undefined') || (distError === true)) {
myOptions = _this.settings.mapSettings;
bounds = new google.maps.LatLngBounds();
}
else if (_this.settings.pagination === true) {
// Update the map to focus on the first point in the new set
var nextPoint = new google.maps.LatLng(locationset[0].lat, locationset[0].lng);
if (page === 0) {
_this.settings.mapSettings.center = originPoint;
myOptions = _this.settings.mapSettings;
}
else {
_this.settings.mapSettings.center = nextPoint;
myOptions = _this.settings.mapSettings;
}
}
else {
_this.settings.mapSettings.center = originPoint;
myOptions = _this.settings.mapSettings;
}
// Create the map
_this.map = new google.maps.Map(document.getElementById(_this.settings.mapID), myOptions);
// Re-center the map when the browser is re-sized
google.maps.event.addDomListener(window, 'resize', function() {
var center = _this.map.getCenter();
google.maps.event.trigger(_this.map, 'resize');
_this.map.setCenter(center);
});
// Add map drag listener if setting is enabled and re-search on drag end
if (_this.settings.dragSearch === true ) {
_this.map.addListener('dragend', function() {
_this.dragSearch(map);
});
}
// Load the map
$this.data(_this.settings.mapID.replace('#', ''), _this.map);
// Map set callback.
if (_this.settings.callbackMapSet) {
_this.settings.callbackMapSet.call(this, _this.map, originPoint, originalZoom, myOptions);
}
// Initialize the infowondow
if ( typeof InfoBubble !== 'undefined' && _this.settings.infoBubble !== null ) {
var infoBubbleSettings = _this.settings.infoBubble;
infoBubbleSettings.map = _this.map;
infowindow = new InfoBubble(infoBubbleSettings);
} else {
infowindow = new google.maps.InfoWindow();
}
// Add origin marker if the setting is set
_this.originMarker(_this.map, origin, originPoint);
// Handle pagination
$(document).on('click.'+pluginName, '.bh-sl-pagination li', function (e) {
e.preventDefault();
// Run paginationChange
_this.paginationChange($(this).attr('data-page'));
});
// Inline directions
_this.inlineDirections(_this.map, origin);
// Add markers and infowindows loop
for (var y = 0; y <= storeNumToShow - 1; y++) {
var letter = '';
if (page > 0) {
letter = String.fromCharCode('A'.charCodeAt(0) + (storeStart + y));
}
else {
letter = String.fromCharCode('A'.charCodeAt(0) + y);
}
var point = new google.maps.LatLng(locationset[y].lat, locationset[y].lng);
marker = _this.createMarker(point, locationset[y].name, locationset[y].address, letter, _this.map, locationset[y].category);
marker.set('id', y);
markers[y] = marker;
if ((_this.settings.fullMapStart === true && firstRun === true) || (_this.settings.mapSettings.zoom === 0) || (typeof origin === 'undefined') || (distError === true)) {
bounds.extend(point);
}
// Pass variables to the pop-up infowindows
_this.createInfowindow(marker, null, infowindow, storeStart, page);
}
// Center and zoom if no origin or zoom was provided, or distance of first marker is greater than distanceAlert
if ((_this.settings.fullMapStart === true && firstRun === true) || (_this.settings.mapSettings.zoom === 0) || (typeof origin === 'undefined') || (distError === true)) {
_this.map.fitBounds(bounds);
}
// Create the links that focus on the related marker
var locList = $('.' + _this.settings.locationList + ' ul');
locList.empty();
// Set up the location list markup
if (firstRun && _this.settings.fullMapStartListLimit !== false && !isNaN(_this.settings.fullMapStartListLimit) && _this.settings.fullMapStartListLimit !== -1) {
for (var m = 0; m < _this.settings.fullMapStartListLimit; m++) {
var currentMarker = markers[m];
_this.listSetup(currentMarker, storeStart, page);
}
} else {
$(markers).each(function (x) {
var currentMarker = markers[x];
_this.listSetup(currentMarker, storeStart, page);
});
}
// MarkerClusterer setup
if ( typeof MarkerClusterer !== 'undefined' && _this.settings.markerCluster !== null ) {
var markerCluster = new MarkerClusterer(_this.map, markers, _this.settings.markerCluster);
}
// Handle clicks from the list
_this.listClick(_this.map, infowindow, storeStart, page);
// Add the list li background colors - this wil be dropped in a future version in favor of CSS
$('.' + _this.settings.locationList + ' ul > li:even').css('background', _this.settings.listColor1);
$('.' + _this.settings.locationList + ' ul > li:odd').css('background', _this.settings.listColor2);
// Visible markers list
_this.visibleMarkersList(_this.map, markers);
// Modal ready callback
if (_this.settings.modal === true && _this.settings.callbackModalReady) {
_this.settings.callbackModalReady.call(this);
}
// Filters callback
if (_this.settings.callbackFilters) {
_this.settings.callbackFilters.call(this, filters);
}
},
/**
* console.log helper function
*
* http://www.briangrinstead.com/blog/console-log-helper-function
*/
writeDebug: function () {
if (window.console && this.settings.debug) {
// Only run on the first time through - reset this function to the appropriate console.log helper
if (Function.prototype.bind) {
this.writeDebug = Function.prototype.bind.call(console.log, console, 'StoreLocator :');
} else {
this.writeDebug = function () {
arguments[0] = 'StoreLocator : ' + arguments[0];
Function.prototype.apply.call(console.log, console, arguments);
};
}
this.writeDebug.apply(this, arguments);
}
}
});
// A really lightweight plugin wrapper around the constructor,
// preventing against multiple instantiations and allowing any
// public function (ie. a function whose name doesn't start
// with an underscore) to be called via the jQuery plugin,
// e.g. $(element).defaultPluginName('functionName', arg1, arg2)
$.fn[ pluginName ] = function (options) {
var args = arguments;
// Is the first parameter an object (options), or was omitted, instantiate a new instance of the plugin
if (options === undefined || typeof options === 'object') {
return this.each(function () {
// Only allow the plugin to be instantiated once, so we check that the element has no plugin instantiation yet
if (!$.data(this, 'plugin_' + pluginName)) {
// If it has no instance, create a new one, pass options to our plugin constructor, and store the plugin instance in the elements jQuery data object.
$.data(this, 'plugin_' + pluginName, new Plugin( this, options ));
}
});
// Treat this as a call to a public method
} else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') {
// Cache the method call to make it possible to return a value
var returns;
this.each(function () {
var instance = $.data(this, 'plugin_' + pluginName);
// Tests that there's already a plugin-instance and checks that the requested public method exists
if (instance instanceof Plugin && typeof instance[options] === 'function') {
// Call the method of our plugin instance, and pass it the supplied arguments.
returns = instance[options].apply( instance, Array.prototype.slice.call( args, 1 ) );
}
// Allow instances to be destroyed via the 'destroy' method
if (options === 'destroy') {
$.data(this, 'plugin_' + pluginName, null);
}
});
// If the earlier cached method gives a value back return the value, otherwise return this to preserve chainability.
return returns !== undefined ? returns : this;
}
};
})(jQuery, window, document);