p;)e--}s[2*i+n]===p?w(r,s,i,e):w(r,s,++e,a),e<=o&&(i=e+1),o<=e&&(a=e-1)}}function w(t,e,r,s){y(t,r,s),y(e,2*r,2*s),y(e,2*r+1,2*s+1)}function y(t,e,r){var s=t[e];t[e]=t[r],t[r]=s}function M(t,e,r,s){t-=r,r=e-s;return t*t+r*r}const U={minZoom:0,maxZoom:16,minPoints:2,radius:40,extent:512,nodeSize:64,log:!1,generateId:!1,reduce:null,map:t=>t},x=Math.fround||(m=new Float32Array(1),t=>(m[0]=+t,m[0])),C=3,P=5,_=6;class E{constructor(t){this.options=Object.assign(Object.create(U),t),this.trees=new Array(this.options.maxZoom+1),this.stride=this.options.reduce?7:6,this.clusterProps=[]}load(e){const{log:r,minZoom:s,maxZoom:o}=this.options,i=(r&&console.time("total time"),`prepare ${e.length} points`);r&&console.time(i),this.points=e;var a=[];for(let t=0;t=s;t--){const s=+Date.now();n=this.trees[t]=this._createTree(this._cluster(n,t)),r&&console.log("z%d: %d clusters in %dms",t,n.numItems,+Date.now()-s)}return r&&console.timeEnd("total time"),this}getClusters(t,e){let r=((t[0]+180)%360+360)%360-180;var s=Math.max(-90,Math.min(90,t[1]));let o=180===t[2]?180:((t[2]+180)%360+360)%360-180;var i=Math.max(-90,Math.min(90,t[3]));if(360<=t[2]-t[0])r=-180,o=180;else if(r>o){const t=this.getClusters([r,s,180,i],e),a=this.getClusters([-180,s,o,i],e);return t.concat(a)}const a=this.trees[this._limitZoom(e)],n=a.range(L(r),O(i),L(o),O(s)),h=a.data,l=[];for(const t of n){const e=this.stride*t;l.push(1=i.length)throw new Error(s);var a=this.options.radius/(this.options.extent*Math.pow(2,r-1)),n=i[e*this.stride],h=i[e*this.stride+1],l=[];for(const e of o.within(n,h,a)){const r=e*this.stride;i[4+r]===t&&l.push(1a&&(o+=l[e+P])}if(o>f&&o>=r){let t,e=n*f,r=d*f,s=-1;var p,m=((i/c|0)<<5)+(a+1)+this.points.length;for(const n of g){const u=n*c;l[2+u]<=a||(l[2+u]=a,p=l[u+P],e+=l[u]*p,r+=l[1+u]*p,l[4+u]=m,h&&(t||(t=this._map(l,i,!0),s=this.clusterProps.length,this.clusterProps.push(t)),h(t,this._map(l,u))))}l[i+4]=m,u.push(e/o,r/o,1/0,m,-1,o),h&&u.push(s)}else{for(let t=0;t>5}_getOriginZoom(t){return(t-this.points.length)%32}_map(t,e,r){if(1<0?0:1{var e=n.getPosition(t);return{type:"Feature",geometry:{type:"Point",coordinates:[e.lng(),e.lat()]},properties:{marker:t}}});this.superCluster.load(r)}return e||(this.state.zoom<=this.maxZoom||r.zoom<=this.maxZoom)&&(e=!g(this.state,r)),this.state=r,e&&(this.clusters=this.cluster(t)),{clusters:this.clusters,changed:e}}cluster(t){t=t.map;return this.superCluster.getClusters([-180,-90,180,90],Math.round(t.getZoom())).map(t=>this.transformCluster(t))}transformCluster(t){var{geometry:{coordinates:[t,e]},properties:r}=t;return r.cluster?new a({markers:this.superCluster.getLeaves(r.cluster_id,1/0).map(t=>t.properties.marker),position:{lat:e,lng:t}}):(e=r.marker,new a({markers:[e],position:n.getPosition(e)}))}}class I{constructor(t,e){this.markers={sum:t.length};var t=e.map(t=>t.count),r=t.reduce((t,e)=>t+e,0);this.clusters={count:e.length,markers:{mean:r/e.length,sum:r,min:Math.min(...t),max:Math.max(...t)}}}}class T{render(t,e,r){var{count:t,position:s}=t,o=`
+
+
+
+${t}
+ `,i=`Cluster of ${t} markers`,a=Number(google.maps.Marker.MAX_ZINDEX)+t;if(n.isAdvancedMarkerAvailable(r)){const t=(new DOMParser).parseFromString(o,"image/svg+xml").documentElement,e=(t.setAttribute("transform","translate(0 25)"),{map:r,position:s,zIndex:a,title:i,content:t});return new google.maps.marker.AdvancedMarkerElement(e)}e={position:s,zIndex:a,title:i,icon:{url:"data:image/svg+xml;base64,"+btoa(o),anchor:new google.maps.Point(25,25)}};return new google.maps.Marker(e)}}class j{constructor(){var t,e=j,r=google.maps.OverlayView;for(t in r.prototype)e.prototype[t]=r.prototype[t]}}o.MarkerClustererEvents=void 0,(d=o.MarkerClustererEvents||(o.MarkerClustererEvents={})).CLUSTERING_BEGIN="clusteringbegin",d.CLUSTERING_END="clusteringend",d.CLUSTER_CLICK="click";const S=(t,e,r)=>{r.fitBounds(e.bounds)};return o.AbstractAlgorithm=t,o.AbstractViewportAlgorithm=e,o.Cluster=a,o.ClusterStats=I,o.DefaultRenderer=T,o.GridAlgorithm=class extends e{constructor(t){var{maxDistance:e=4e4,gridSize:r=40}=t;super(i(t,["maxDistance","gridSize"])),this.clusters=[],this.state={zoom:-1},this.maxDistance=e,this.gridSize=r}calculate(t){var{markers:t,map:e,mapCanvasProjection:r}=t,s={zoom:e.getZoom()};let o=!1;return this.state.zoom>=this.maxZoom&&s.zoom>=this.maxZoom||(o=!g(this.state,s)),this.state=s,e.getZoom()>=this.maxZoom?{clusters:this.noop({markers:t}),changed:o}:{clusters:this.cluster({markers:h(e,r,t,this.viewportPadding),map:e,mapCanvasProjection:r})}}cluster(t){let{markers:e,map:r,mapCanvasProjection:s}=t;return this.clusters=[],e.forEach(t=>{this.addToClosestCluster(t,r,s)}),this.clusters}addToClosestCluster(e,t,r){let s=this.maxDistance,o=null;for(let t=0;t{this.addMarker(t,!0)}),e||this.render()}removeMarker(t,e){var r=this.markers.indexOf(t);return-1!==r&&(n.setMap(t,null),this.markers.splice(r,1),e||this.render(),!0)}removeMarkers(t,e){let r=!1;return t.forEach(t=>{r=this.removeMarker(t,!0)||r}),r&&!e&&this.render(),r}clearMarkers(t){this.markers.length=0,t||this.render()}render(){const t=this.getMap();if(t instanceof google.maps.Map&&t.getProjection()){google.maps.event.trigger(this,o.MarkerClustererEvents.CLUSTERING_BEGIN,this);var{clusters:e,changed:r}=this.algorithm.calculate({markers:this.markers,map:t,mapCanvasProjection:this.getProjection()});if(r||null==r){const o=new Set;for(const t of e)1==t.markers.length&&o.add(t.markers[0]);const t=[];for(const s of this.clusters)null!=s.marker&&(1==s.markers.length?o.has(s.marker)||n.setMap(s.marker,null):t.push(s.marker));this.clusters=e,this.renderClusters(),requestAnimationFrame(()=>t.forEach(t=>n.setMap(t,null)))}google.maps.event.trigger(this,o.MarkerClustererEvents.CLUSTERING_END,this)}}onAdd(){this.idleListener=this.getMap().addListener("idle",this.render.bind(this)),this.render()}onRemove(){google.maps.event.removeListener(this.idleListener),this.reset()}reset(){this.markers.forEach(t=>n.setMap(t,null)),this.clusters.forEach(t=>t.delete()),this.clusters=[]}renderClusters(){const t=new I(this.markers,this.clusters),r=this.getMap();this.clusters.forEach(e=>{1===e.markers.length?e.marker=e.markers[0]:(e.marker=this.renderer.render(e,t,r),e.markers.forEach(t=>n.setMap(t,null)),this.onClusterClick&&e.marker.addListener("click",t=>{google.maps.event.trigger(this,o.MarkerClustererEvents.CLUSTER_CLICK,e),this.onClusterClick(t,e,r)})),n.setMap(e.marker,r)})}},o.MarkerUtils=n,o.NoopAlgorithm=class extends t{constructor(t){super(i(t,[]))}calculate(t){var{markers:t,map:e,mapCanvasProjection:r}=t;return{clusters:this.cluster({markers:t,map:e,mapCanvasProjection:r}),changed:!1}}cluster(t){return this.noop(t)}},o.SuperClusterAlgorithm=Z,o.SuperClusterViewportAlgorithm=class extends e{constructor(t){var{maxZoom:e,radius:r=60,viewportPadding:s=60}=t,t=i(t,["maxZoom","radius","viewportPadding"]);super({maxZoom:e,viewportPadding:s}),this.superCluster=new E(Object.assign({maxZoom:this.maxZoom,radius:r},t)),this.state={zoom:-1,view:[0,0,0,0]}}calculate(t){const e={zoom:Math.round(t.map.getZoom()),view:s(t.map.getBounds(),t.mapCanvasProjection,this.viewportPadding)};let r=!g(this.state,e);if(!g(t.markers,this.markers)){r=!0,this.markers=[...t.markers];const e=this.markers.map(t=>{var e=n.getPosition(t);return{type:"Feature",geometry:{type:"Point",coordinates:[e.lng(),e.lat()]},properties:{marker:t}}});this.superCluster.load(e)}return r&&(this.clusters=this.cluster(t),this.state=e),{clusters:this.clusters,changed:r}}cluster(t){var{map:t,mapCanvasProjection:e}=t,t={zoom:Math.round(t.getZoom()),view:s(t.getBounds(),e,this.viewportPadding)};return this.superCluster.getClusters(t.view,t.zoom).map(t=>this.transformCluster(t))}transformCluster(t){var{geometry:{coordinates:[t,e]},properties:r}=t;return r.cluster?new a({markers:this.superCluster.getLeaves(r.cluster_id,1/0).map(t=>t.properties.marker),position:{lat:e,lng:t}}):(e=r.marker,new a({markers:[e],position:n.getPosition(e)}))}},o.defaultOnClusterClickHandler=S,o.distanceBetweenPoints=u,o.extendBoundsToPaddedViewport=l,o.extendPixelBounds=c,o.filterMarkersToPaddedViewport=h,o.getPaddedViewport=s,o.noop=r,o.pixelBoundsToLatLngBounds=p,Object.defineProperty(o,"__esModule",{value:!0}),o}({});
\ No newline at end of file
diff --git a/dist/assets/js/plugins/storeLocator/jquery.storelocator.js b/dist/assets/js/plugins/storeLocator/jquery.storelocator.js
index 1db4cc8..07fbed0 100644
--- a/dist/assets/js/plugins/storeLocator/jquery.storelocator.js
+++ b/dist/assets/js/plugins/storeLocator/jquery.storelocator.js
@@ -1,106 +1,151 @@
-/*! jQuery Google Maps Store Locator - v2.0.5 - 2015-01-04
+/*! jQuery Google Maps Store Locator - v3.4.1 - 2024-12-20
* http://www.bjornblog.com/web/jquery-store-locator-plugin
-* Copyright (c) 2015 Bjorn Holine; Licensed MIT */
+* Copyright (c) 2024 Bjorn Holine; Licensed MIT */
;(function ($, window, document, undefined) {
'use strict';
var pluginName = 'storeLocator';
+ var googleMapsScriptIsInjected = false;
+ var googleMapsAPIPromise = null;
// Only allow for one instantiation of this script
if (typeof $.fn[pluginName] !== 'undefined') {
return;
}
- // Variables used across multiple methods
- var $this, listTemplate, infowindowTemplate, dataTypeRead, originalOrigin, originalData, originalZoom, dataRequest, searchInput, addressInput, olat, olng, storeNum, directionsDisplay, directionsService;
+ // 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, reload, nameAttrs, originalFilterVals, paginationPage, locationsTotal;
var featuredset = [], locationset = [], normalset = [], markers = [];
- var filters = {}, locationData = {}, GeoCodeCalc = {}, mappingObj = {};
+ var filters = {}, locationData = {}, GeoCodeCalc = {}, mappingObj = {}, disabledFilterVals = {};
// 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' : {
+ 'ajaxData' : null,
+ 'altDistanceNoResult' : false,
+ 'apiKey' : null,
+ 'autoComplete' : false,
+ 'autoCompleteDisableListener': false,
+ 'autoCompleteOptions' : {},
+ 'autoGeocode' : false,
+ 'bounceMarker' : true, // Deprecated.
+ 'catMarkers' : null,
+ 'dataLocation' : 'data/locations.json',
+ 'dataRaw' : null,
+ 'dataType' : 'json',
+ 'debug' : false,
+ 'defaultLat' : null,
+ 'defaultLng' : null,
+ 'defaultLoc' : false,
+ 'disableAlphaMarkers' : false,
+ 'distanceAlert' : 60,
+ 'dragSearch' : false,
+ 'exclusiveFiltering' : false,
+ 'exclusiveTax' : null,
+ 'featuredDistance' : null,
+ 'featuredLocations' : false,
+ 'fullMapStart' : false,
+ 'fullMapStartBlank' : false,
+ 'fullMapStartListLimit' : false,
+ 'infoBubble' : null,
+ 'inlineDirections' : false,
+ 'lazyLoadMap' : false,
+ 'lengthUnit' : 'm',
+ 'listColor1' : '#ffffff',
+ 'listColor2' : '#eeeeee',
+ 'loading' : false,
+ 'locationsPerPage' : 10,
+ 'mapSettings' : {
+ mapTypeId: 'roadmap',
zoom : 12,
- mapTypeId: google.maps.MapTypeId.ROADMAP
- },
- 'markerImg' : null,
- 'markerDim' : null,
- 'catMarkers' : null,
- 'lengthUnit' : 'm',
- 'storeLimit' : 26,
- 'distanceAlert' : 60,
- 'dataType' : 'xml',
- 'dataLocation' : 'data/locations.xml',
- '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,
- 'autoGeocode' : false,
- 'maxDistance' : false,
- 'maxDistanceID' : 'bh-sl-maxdistance',
- 'fullMapStart' : 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',
+ },
+ 'mapSettingsID' : '',
+ 'markerCluster' : null,
+ 'markerImg' : null,
+ 'markerDim' : null,
+ 'maxDistance' : false,
+ 'modal' : false,
+ 'nameAttribute' : 'name',
+ 'nameSearch' : false,
+ 'noForm' : false,
+ 'openNearest' : false,
+ 'originMarker' : false,
+ 'originMarkerDim' : null,
+ 'originMarkerImg' : null,
+ 'pagination' : false,
+ 'querystringParams' : false,
+ 'selectedMarkerImg' : null,
+ 'selectedMarkerImgDim' : null,
+ 'sessionStorage' : false,
+ 'slideMap' : true,
+ 'sortBy' : null,
+ 'storeLimit' : 26,
+ 'taxonomyFilters' : null,
+ 'visibleMarkersList' : false,
+ 'xmlElement' : 'marker',
+ // HTML elements
+ 'addressID' : 'bh-sl-address',
+ 'closeIcon' : 'bh-sl-close-icon',
+ 'formContainer' : 'bh-sl-form-container',
+ 'formID' : 'bh-sl-user-location',
+ 'geocodeID' : null,
+ 'lengthSwapID' : 'bh-sl-length-swap',
+ 'loadingContainer' : 'bh-sl-loading',
+ 'locationList' : 'bh-sl-loc-list',
+ 'mapID' : 'bh-sl-map',
+ 'maxDistanceID' : 'bh-sl-maxdistance',
+ 'modalContent' : 'bh-sl-modal-content',
+ 'modalWindow' : 'bh-sl-modal-window',
+ 'orderID' : 'bh-sl-order',
+ 'overlay' : 'bh-sl-overlay',
+ 'regionID' : 'bh-sl-region',
+ 'searchID' : 'bh-sl-search',
+ 'sortID' : 'bh-sl-sort',
+ 'taxonomyFiltersContainer': 'bh-sl-filters-container',
+ // Templates
'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',
- 'querystringParams' : false,
- 'callbackNotify' : null,
- 'callbackBeforeSend' : null,
- 'callbackSuccess' : null,
- 'callbackModalOpen' : null,
- 'callbackModalReady' : null,
- 'callbackModalClose' : null,
- 'callbackJsonp' : null,
- 'callbackPageChange' : null,
- 'callbackDirectionsRequest': null,
- 'callbackCloseDirections' : null,
- 'callbackNoResults' : null,
- 'callbackListClick' : null,
- 'callbackMarkerClick' : null,
+ // Callbacks
+ 'callbackAutoGeoSuccess' : null,
+ 'callbackBeforeMapInject' : null,
+ 'callbackBeforeSend' : null,
+ 'callbackCloseDirections' : null,
+ 'callbackCreateMarker' : null,
+ 'callbackDirectionsRequest' : null,
+ 'callbackFilters' : null,
+ 'callbackFormVals' : null,
+ 'callbackGeocodeRestrictions': null,
+ 'callbackJsonp' : null,
+ 'callbackListClick' : null,
+ 'callbackMapSet' : null,
+ 'callbackMarkerClick' : null,
+ 'callbackModalClose' : null,
+ 'callbackModalOpen' : null,
+ 'callbackModalReady' : null,
+ 'callbackNearestLoc' : null,
+ 'callbackNoResults' : null,
+ 'callbackNotify' : null,
+ 'callbackOrder' : null,
+ 'callbackPageChange' : null,
+ 'callbackRegion' : null,
+ 'callbackSorting' : null,
+ 'callbackSuccess' : 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'
+ '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 ',
+ 'kilometerLang' : 'kilometer',
+ 'kilometersLang' : 'kilometers',
+ 'mileLang' : 'mile',
+ 'milesLang' : 'miles',
+ 'noResultsTitle' : 'No results',
+ 'noResultsDesc' : 'No locations were found with the given criteria. Please modify your selections or input.',
+ 'nextPage' : 'Next »',
+ 'prevPage' : '« Prev'
};
// Plugin constructor
@@ -110,16 +155,51 @@
this.settings = $.extend({}, defaults, options);
this._defaults = defaults;
this._name = pluginName;
- this.init();
+
+ // Add Map ID to map settings if set.
+ if (this.settings.mapSettingsID !== '') {
+ this.settings.mapSettings.mapId = this.settings.mapSettingsID;
+ }
+
+ // Load Google Maps API when lazy load is enabled.
+ if (this.settings.lazyLoadMap && this.settings.apiKey !== null && typeof google === 'undefined') {
+ var _this = this;
+ var optionsQuery = {};
+ var loadMap = false;
+
+ // Load new marker library.
+ optionsQuery.libraries = 'marker';
+
+ // Autocomplete.
+ if (this.settings.autoComplete === true) {
+ optionsQuery.libraries = 'places,marker';
+ }
+
+ // Allow callback to resolve map loading when set.
+ if (this.settings.callbackBeforeMapInject) {
+ new Promise(function (resolve, reject) {
+ _this.settings.callbackBeforeMapInject.call(this, options, resolve);
+ }).then(function () {
+ _this.triggerMapLoad(optionsQuery)
+ });
+ } else {
+ _this.triggerMapLoad(optionsQuery)
+ }
+ } else {
+ 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
@@ -138,10 +218,8 @@
dataTypeRead = this.settings.dataType;
}
- // Set up the directionsService if it's true
- if(this.settings.inlineDirections === true) {
- directionsDisplay = new google.maps.DirectionsRenderer();
- directionsService = new google.maps.DirectionsService();
+ // Add directions panel if enabled
+ if (this.settings.inlineDirections === true) {
$('.' + this.settings.locationList).prepend('
');
}
@@ -150,19 +228,28 @@
// Add Handlebars helper for handling URL output
Handlebars.registerHelper('niceURL', function(url) {
- if(url){
+ if (url) {
return url.replace('https://', '').replace('http://', '');
}
});
+ // Handle distance changes on select
+ if (this.settings.maxDistance === true) {
+ this.distanceFiltering();
+ }
+
// Do taxonomy filtering if set
if (this.settings.taxonomyFilters !== null) {
this.taxonomyFiltering();
}
+ // Do sorting and ordering if set.
+ this.sorting();
+ this.order();
+
// 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
+ // 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);
@@ -173,21 +260,110 @@
$('.' + 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 && this.settings.autoCompleteDisableListener !== true) {
+ autoPlaces.addListener('place_changed', function(e) {
+ _this.processForm(e);
+ });
+ }
+ }
+
// Load the templates and continue from there
this._loadTemplates();
},
+ /**
+ * Trigger async map loading
+ */
+ triggerMapLoad: function(optionsQuery) {
+ this.writeDebug('triggerMapLoad');
+ var _this = this;
+
+ this.loadMapsAPI(this.settings.apiKey, optionsQuery)
+ .then(function (map) {
+ _this.map = map;
+ _this.init();
+ });
+ },
+
+ /**
+ * Inject Google Maps script
+ *
+ * @param {Object} options Options query object to pass as query string parameters to Google Maps.
+ */
+ injectGoogleMapsScript: function (options) {
+ this.writeDebug('injectGoogleMapsScript');
+ options = (typeof options !== 'undefined') ? options : {};
+
+ if (googleMapsScriptIsInjected) {
+ throw new Error('Google Maps API is already loaded.');
+ }
+
+ var optionsQuery = Object.keys(options)
+ .map(k => encodeURIComponent(k) + '=' + encodeURIComponent(options[k]))
+ .join('&');
+ var apiURL = 'https://maps.googleapis.com/maps/api/js?' + optionsQuery;
+ var mapScript = document.createElement('script');
+ mapScript.setAttribute('src', apiURL);
+ mapScript.setAttribute('async', '');
+ mapScript.setAttribute('defer', '');
+
+ // Append the script to the document head.
+ document.head.appendChild(mapScript);
+ googleMapsScriptIsInjected = true;
+ },
+
+ /**
+ * Load Google Maps API
+ *
+ * @param {string} apiKey Google Maps JavaScript API key.
+ * @param {Object} options Options query object to pass as query string parameters to Google Maps.
+ *
+ * @returns {Promise}
+ */
+ loadMapsAPI: function (apiKey, options) {
+ this.writeDebug('loadMapsAPI');
+ options = (typeof options !== 'undefined') ? options : {};
+ var _this = this;
+
+ if (!googleMapsAPIPromise) {
+ googleMapsAPIPromise = new Promise(function (resolve, reject) {
+ try {
+ window.onGoogleMapsAPILoaded = resolve;
+
+ _this.injectGoogleMapsScript({
+ key: apiKey,
+ loading: 'async',
+ callback: 'onGoogleMapsAPILoaded',
+ ...options,
+ });
+ } catch (error) {
+ reject(error);
+ }
+ }).then(function () { window.google.maps });
+ }
+
+ return googleMapsAPIPromise;
+ },
+
/**
* Destroy
- * Note: The Google map is not destroyed here because Google recommends using a single instance and reusing it (it's not really supported)
+ * 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) {
+ if (markers.length) {
for(var i = 0; i <= markers.length; i++) {
google.maps.event.removeListener(markers[i]);
}
@@ -195,7 +371,7 @@
// Remove markup
$('.' + this.settings.locationList + ' ul').empty();
- if($mapDiv.hasClass('bh-sl-map-open')) {
+ if ($mapDiv.hasClass('bh-sl-map-open')) {
$mapDiv.empty().removeClass('bh-sl-map-open');
}
@@ -216,28 +392,100 @@
// 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 ) {
+
+ 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 a');
+ }
+ },
+
+ /**
+ * 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();
+ reload = true;
+
+ if (this.settings.taxonomyFilters !== null) {
+ this.formFiltersReset();
+ this.resetDisabledFilterVals();
+ 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);
}
@@ -245,27 +493,60 @@
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) ))));
},
+ /**
+ * Range helper function for coordinate validation
+ *
+ * @param min {number} minimum number allowed
+ * @param num {number} number to check
+ * @param max {number} maximum number allowed
+ *
+ * @returns {boolean}
+ */
+ inRange: function(min, num, max){
+ this.writeDebug('inRange',arguments);
+ num = Math.abs(num);
+ return isFinite(num) && (num >= min) && (num <= max);
+ },
+
+ /**
+ * Coordinate validation
+ *
+ * @param lat {number} latitude
+ * @param lng {number} longitude
+ *
+ * @returns {boolean}
+ */
+ coordinatesInRange: function (lat, lng) {
+ this.writeDebug('coordinatesInRange',arguments);
+ return this.inRange(-90, lat, 90) && this.inRange(-180, lng, 180);
+ },
+
/**
* 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 + '=([^]*)'),
@@ -274,10 +555,20 @@
}
},
+ /**
+ * 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 = '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.
';
@@ -291,7 +582,7 @@
source = template;
infowindowTemplate = Handlebars.compile(source);
}),
-
+
// KML locations list
$.get(this.settings.KMLlistTemplatePath, function (template) {
source = template;
@@ -327,7 +618,7 @@
source = template;
infowindowTemplate = Handlebars.compile(source);
}),
-
+
// Locations list
$.get(this.settings.listTemplatePath, function (template) {
source = template;
@@ -349,6 +640,7 @@
* 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();
@@ -362,13 +654,14 @@
* 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('keyup.'+pluginName, function (e) {
+ $(document).on('keydown.'+pluginName, function (e) {
if (e.keyCode === 13 && $('#' + _this.settings.addressID).is(':focus')) {
_this.processForm(e);
}
@@ -379,97 +672,246 @@
_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();
+ });
+ }
+
+ // Track changes to the address search field.
+ $('#' + this.settings.addressID).on('change.'+pluginName, function () {
+ originalFilterVals = undefined;
+ disabledFilterVals = {};
+
+ // Unset origin tracking if input field is removed.
+ if (
+ $.trim($('#' + _this.settings.addressID).val()) === '' &&
+ (typeof searchInput === 'undefined' || searchInput === '')
+ ) {
+
+ // Reset the origin, mapping object, and disabled filter values.
+ if (_this.settings.taxonomyFilters !== null && _this.settings.exclusiveFiltering === false) {
+ olat = undefined;
+ olng = undefined;
+ originalOrigin = undefined;
+ mappingObj = {};
+ _this.resetDisabledFilterVals();
+ _this.taxonomyFiltersInit();
+ _this.mapping(null);
+ }
+ }
+ });
},
/**
* 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
+ * @param map (object} Google Maps object.
+ *
* @returns {Object} deferred object
*/
- _getData: function (lat, lng, address) {
- var _this = this;
- var d = $.Deferred();
-
+ _getData: function (lat, lng, address, geocodeData, map) {
+ 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);
+ this.settings.callbackBeforeSend.call(this, lat, lng, address, formattedAddress, northEast, southWest, map);
}
- // Loading
- if(this.settings.loading === true){
- $('.' + this.settings.formContainer).append('
');
+ // 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 JSON.parse(_this.settings.dataRaw);
+ }
+ else {
+ return [];
+ }
+
+ }
}
+ // Remote data
+ else {
+ var d = $.Deferred();
+
+ // Loading
+ if (this.settings.loading === true) {
+ $('.' + this.settings.formContainer).append('
');
+ }
- // AJAX request
- $.ajax({
- type : 'GET',
- url : this.settings.dataLocation + (this.settings.dataType === 'jsonp' ? (this.settings.dataLocation.match(/\?/) ? '&' : '?') + 'callback=?' : ''),
- // Passing the lat, lng, and address with the AJAX request so they can optionally be used by back-end languages
- data: {
+ // Data to pass with the AJAX request
+ var ajaxData = {
'origLat' : lat,
'origLng' : lng,
- 'origAddress': address
- },
- dataType : dataTypeRead,
- jsonpCallback: (this.settings.dataType === 'jsonp' ? this.settings.callbackJsonp : null)
- }).done(function (p) {
- d.resolve(p);
+ 'origAddress': address,
+ 'formattedAddress': formattedAddress,
+ 'boundsNorthEast' : northEast,
+ 'boundsSouthWest' : southWest
+ };
- // Loading remove
- if(_this.settings.loading === true){
- $('.' + _this.settings.formContainer + ' .' + _this.settings.loadingContainer).remove();
- }
- }).fail(d.reject);
- return d.promise();
+ // Set up extra object for custom extra data to be passed with the AJAX request
+ if (this.settings.ajaxData !== null && typeof this.settings.ajaxData === 'object') {
+ $.extend(ajaxData, this.settings.ajaxData);
+ }
+
+ // 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 : ajaxData,
+ 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 () {
- var _this = this;
- // If a default location is set
- if (this.settings.defaultLoc === true) {
- // 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) {
- var originAddress = data.address;
- mappingObj.lat = _this.settings.defaultLat;
- mappingObj.lng = _this.settings.defaultLng;
- mappingObj.origin = originAddress;
- _this.mapping(mappingObj);
- } else {
- // Unable to geocode
- _this.notify(_this.settings.addressErrorAlert);
- }
+ this.writeDebug('_start');
+ var _this = this,
+ doAutoGeo = this.settings.autoGeocode,
+ latlng;
+
+ // 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
+ window.addEventListener('resize', function() {
+ var center = _this.map.getCenter();
+ google.maps.event.trigger(_this.map, 'resize');
+ _this.map.setCenter(center);
});
- }
- // If show full map option is true
- if (this.settings.fullMapStart === true) {
- if(this.settings.querystringParams === true && this.getQueryString(this.settings.addressID) || this.getQueryString(this.settings.searchID)) {
- this.processForm(null);
- }
- else {
- this.mapping(null);
+ // 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 && this.settings.defaultLoc === false) {
+ 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 geolocation API option
- if (this.settings.autoGeocode === true) {
- if (navigator.geolocation) {
- navigator.geolocation.getCurrentPosition(function(position){
- // Have to do this to get around scope issues
- _this.autoGeocodeQuery(position);
- }, function(error){
- _this._autoGeocodeError(error);
- });
- }
+ // 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));
+ }
+
+ // Callback
+ if (_this.settings.callbackAutoGeoSuccess) {
+ _this.settings.callbackAutoGeoSuccess.call(this, pos);
+ }
+
+ _this.autoGeocodeQuery(pos);
+ }, function(error){
+ _this._autoGeocodeError(error);
+ });
}
},
@@ -477,7 +919,7 @@
* Geocode function used to geocode the origin (entered location)
*/
googleGeocode: function (thisObj) {
- var _this = thisObj;
+ thisObj.writeDebug('googleGeocode',arguments);
var geocoder = new google.maps.Geocoder();
this.geocode = function (request, callbackFunction) {
geocoder.geocode(request, function (results, status) {
@@ -485,6 +927,7 @@
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);
@@ -498,7 +941,7 @@
* Reverse geocode to get address for automatic options needed for directions link
*/
reverseGoogleGeocode: function (thisObj) {
- var _this = thisObj;
+ thisObj.writeDebug('reverseGoogleGeocode',arguments);
var geocoder = new google.maps.Geocoder();
this.geocode = function (request, callbackFunction) {
geocoder.geocode(request, function (results, status) {
@@ -506,6 +949,7 @@
if (results[0]) {
var result = {};
result.address = results[0].formatted_address;
+ result.fullResult = results[0];
callbackFunction(result);
}
} else {
@@ -521,9 +965,11 @@
*
* @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);
},
@@ -531,9 +977,11 @@
* 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;
@@ -544,16 +992,18 @@
/**
* 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) {
+ if (obj.hasOwnProperty(key)) {
+ if (obj[key] !== '' && obj[key].length !== 0) {
objTest = false;
}
}
@@ -562,18 +1012,36 @@
return objTest;
},
+ /**
+ * Checks to see if only a single taxonomy group has a selected value
+ *
+ * @param obj {Object} the object to check
+ * @param key {string} Key value of current filter group
+ *
+ * @returns {boolean}
+ */
+ hasSingleGroupFilterVal: function(obj, key) {
+ this.writeDebug('hasSingleGroupFilterVal',arguments);
+
+ // Copy the object so the original doesn't change.
+ var objCopy = Object.assign({}, obj);
+
+ return !this.hasEmptyObjectVals(objCopy[key]);
+ },
+
/**
* 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);
@@ -588,13 +1056,15 @@
* @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') {
+ if (key === 'distance' || key === 'altdistance') {
value = this.roundNumber(value, 2);
}
@@ -604,88 +1074,321 @@
},
/**
- * Location distance sorting function
+ * Location alphabetical sorting function
*
* @param locationsarray {array} locationset array
*/
- sortNumerically: function (locationsarray) {
- locationsarray.sort(function (a, b) {
- return ((a.distance < b.distance) ? -1 : ((a.distance > b.distance) ? 1 : 0));
- });
+ sortAlpha: function(locationsarray) {
+ this.writeDebug('sortAlpha',arguments);
+ var property = (this.settings.sortBy.hasOwnProperty('prop') && typeof this.settings.sortBy.prop !== 'undefined') ? this.settings.sortBy.prop : 'name';
+
+ if (this.settings.sortBy.hasOwnProperty('order') && this.settings.sortBy.order.toString() === 'desc') {
+ locationsarray.sort(function (a, b) {
+ return b[property].toLowerCase().localeCompare(a[property].toLowerCase());
+ });
+ } else {
+ locationsarray.sort(function (a, b) {
+ return a[property].toLowerCase().localeCompare(b[property].toLowerCase());
+ });
+ }
},
/**
- * Filter the data with Regex
+ * Location date sorting function
*
- * @param data {array} data array to check for filter values
- * @param filters {Object} taxonomy filters object
- * @returns {boolean}
+ * @param locationsarray {array} locationset array
*/
- filterData: function (data, filters) {
- var filterTest = true;
+ sortDate: function(locationsarray) {
+ this.writeDebug('sortDate',arguments);
+ var property = (this.settings.sortBy.hasOwnProperty('prop') && typeof this.settings.sortBy.prop !== 'undefined') ? this.settings.sortBy.prop : 'date';
- for (var k in filters) {
- if (filters.hasOwnProperty(k)) {
- if (!(new RegExp(filters[k].join(''), 'i').test(data[k]))) {
- filterTest = false;
- }
- }
+ if (this.settings.sortBy.hasOwnProperty('order') && this.settings.sortBy.order.toString() === 'desc') {
+ locationsarray.sort(function (a, b) {
+ return new Date(b[property]).getTime() - new Date(a[property]).getTime();
+ });
+ } else {
+ locationsarray.sort(function (a, b) {
+ return new Date(a[property]).getTime() - new Date(b[property]).getTime();
+ });
}
+ },
- if (filterTest) {
- return true;
+ /**
+ * Location distance sorting function
+ *
+ * @param locationsarray {array} locationset array
+ * @param distanceOverride {boolean} Force sort by distance
+ */
+ sortNumerically: function (locationsarray, distanceOverride) {
+ this.writeDebug('sortNumerically',arguments);
+ var property = (
+ this.settings.sortBy !== null &&
+ this.settings.sortBy.hasOwnProperty('prop') &&
+ typeof this.settings.sortBy.prop !== 'undefined'
+ ) ? this.settings.sortBy.prop : 'distance';
+
+ if (typeof distanceOverride !== 'undefined' && distanceOverride === true) {
+ property = 'distance';
+ }
+
+ if (this.settings.sortBy !== null && this.settings.sortBy.hasOwnProperty('order') && this.settings.sortBy.order.toString() === 'desc') {
+ locationsarray.sort(function (a, b) {
+ return ((b[property] < a[property]) ? -1 : ((b[property] > a[property]) ? 1 : 0));
+ });
+ } else {
+ locationsarray.sort(function (a, b) {
+ return ((a[property] < b[property]) ? -1 : ((a[property] > b[property]) ? 1 : 0));
+ });
}
},
/**
- * Build pagination numbers and next/prev links - private
+ * Alternative sorting setup
*
- * @param currentPage {number}
- * @param totalPages {number}
- * @returns {string}
+ * @param locationsarray {array} locationset array
*/
- _paginationOutput: function(currentPage, totalPages) {
-
- currentPage = parseFloat(currentPage);
- var output = '';
- var nextPage = currentPage + 1;
- var prevPage = currentPage - 1;
+ sortCustom: function (locationsarray) {
+ this.writeDebug('sortCustom',arguments);
- // Previous page
- if( currentPage > 0 ) {
- output += '' + this.settings.prevPage + ' ';
+ // Alphabetically, date, or numeric
+ if (this.settings.sortBy.hasOwnProperty('method') && this.settings.sortBy.method.toString() === 'alpha') {
+ this.sortAlpha(locationsarray);
+ } else if (this.settings.sortBy.hasOwnProperty('method') && this.settings.sortBy.method.toString() === 'date') {
+ this.sortDate(locationsarray);
+ } else {
+ this.sortNumerically(locationsarray);
}
+ },
- // Add the numbers
- for (var p = 0; p < Math.ceil(totalPages); p++) {
- var n = p + 1;
+ /**
+ * Run the matching between regular expression filters and string value
+ *
+ * @param filter {array} One or multiple filters to apply
+ * @param val {string} Value to compare
+ * @param inclusive {boolean} Inclusive (default) or exclusive
+ *
+ * @returns {boolean}
+ */
+ filterMatching: function(filter, val, inclusive) {
+ this.writeDebug('inclusiveFilter',arguments);
+ inclusive = (typeof inclusive !== 'undefined') ? inclusive : true;
+ var applyFilters;
- if (p === currentPage) {
- output += '' + n + ' ';
- }
- else {
- output += '' + n + ' ';
- }
+ // Undefined check.
+ if (typeof val === 'undefined') {
+ return false;
}
- // Next page
- if( nextPage < totalPages ) {
- output += '' + this.settings.nextPage + ' ';
+ // Modify the join depending on inclusive (AND) vs exclusive (OR).
+ if ( true === inclusive ) {
+ applyFilters = filter.join('');
+ } else {
+ applyFilters = filter.join('|');
}
- return output;
+ if ((new RegExp(applyFilters, 'i').test(val.replace(/([.*+?^=!:${}()|\[\]\/\\]|&\s+)/g, '')))) {
+ return true;
+ }
+
+ return false;
},
/**
- * Set up the pagination pages
+ * 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)) {
+ var testResults = [];
+
+ for (var l = 0; l < filters[k].length; l++) {
+
+ // Exclusive filtering
+ if (this.settings.exclusiveFiltering === true || (this.settings.exclusiveTax !== null && Array.isArray(this.settings.exclusiveTax) && this.settings.exclusiveTax.indexOf(k) !== -1)) {
+ testResults[l] = this.filterMatching(filters[k], data[k], false);
+ }
+ // Inclusive filtering
+ else {
+ testResults[l] = this.filterMatching(filters[k], data[k]);
+ }
+ }
+
+ // First handle name search, then standard filtering.
+ if (typeof nameAttrs !== 'undefined' && nameAttrs.indexOf(k) !== -1 && testResults.indexOf(true) !== -1) {
+ return true;
+ } else {
+ if (testResults.indexOf(true) === -1) {
+ filterTest = false;
+ }
+ }
+ }
+ }
+
+ return filterTest;
+ },
+
+ /**
+ * 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 = parseInt(currentPage);
+ totalPages = Math.ceil(totalPages);
+ var pagesStart, pagesEnd;
+ var output = '';
+ var nextPage = currentPage + 1;
+ var prevPage = currentPage - 1;
+ var pagesCutoff = 5;
+ var pagesCeiling = Math.ceil(pagesCutoff / 2);
+ var pagesFloor = Math.floor(pagesCutoff / 2);
+
+ // Determine page numbers to display.
+ if (totalPages < pagesCutoff) {
+ pagesStart = 0;
+ pagesEnd = totalPages;
+ } else if (currentPage >= 0 && currentPage <= pagesCeiling) {
+ pagesStart = 0;
+ pagesEnd = pagesCutoff;
+ } else if ((currentPage + pagesFloor) >= totalPages) {
+ pagesStart = (totalPages - pagesCutoff);
+ pagesEnd = totalPages;
+ } else {
+ pagesStart = (currentPage - pagesCeiling);
+ pagesEnd = (currentPage + pagesFloor);
+ }
+
+ // Previous page
+ if ( currentPage > 0 ) {
+ output += '';
+ output += '' + this.settings.prevPage + ' ';
+ output += ' ';
+ }
+
+ // Additional pages indicator.
+ if ((currentPage + 1) >= pagesCutoff && totalPages > pagesCutoff) {
+ // First page link.
+ output += '';
+ output += '' + 1 + ' ';
+ output += ' ';
+
+ output += '';
+ output += '… ';
+ output += ' ';
+ }
+
+ // Add the numbers
+ for (var p = pagesStart; p < pagesEnd; p++) {
+ var n = p + 1;
+
+ if (p === currentPage) {
+ output += '';
+ output += '' + n + ' ';
+ output += ' ';
+ } else {
+ output += '';
+ output += '' + n + ' ';
+ output += ' ';
+ }
+ }
+
+ // Additional pages indicator.
+ if ((currentPage + pagesCeiling) <= totalPages && totalPages > pagesCutoff) {
+ output += '';
+ output += '… ';
+ output += ' ';
+
+ // Last page link.
+ output += '';
+ output += '' + totalPages + ' ';
+ output += ' ';
+ }
+
+ // Next page
+ if ( nextPage < totalPages ) {
+ output += '';
+ output += '' + this.settings.nextPage + ' ';
+ output += ' ';
+ }
+
+ return output;
+ },
+
+ /**
+ * Reset pagination after the input has changed
+ */
+ paginationReset: function() {
+ this.writeDebug('paginationReset',arguments);
+
+ var currentUrl = window.location.href;
+ var url = new URL(currentUrl);
+
+ // Remove the old page in the URL.
+ url.searchParams.delete('bhsl-page');
+
+ // Update the query string param to match the new value.
+ if (history.pushState) {
+ window.history.pushState({path: url.href}, '', url.href);
+ }
+ },
+
+ /**
+ * Determine the total number of pages for pagination
+ */
+ totalPages: function() {
+ this.writeDebug('totalPages',arguments);
+
+ // Location limit.
+ if (
+ typeof originalOrigin !== 'undefined' &&
+ this.settings.storeLimit > 0 &&
+ locationset.length > this.settings.storeLimit
+ ) {
+ return this.settings.storeLimit / this.settings.locationsPerPage;
+ }
+
+ // WP API response after search.
+ if (locationsTotal > 0) {
+ return locationsTotal / this.settings.locationsPerPage;
+ }
+
+ // Unlimited or last page.
+ if (
+ this.settings.storeLimit === -1 ||
+ locationset.length < this.settings.storeLimit
+ ) {
+ return locationset.length / this.settings.locationsPerPage;
+ } else {
+ return this.settings.storeLimit / this.settings.locationsPerPage;
+ }
+ },
+
+ /**
+ * Set up the pagination pages
*
* @param currentPage {number} optional current page
*/
paginationSetup: function (currentPage) {
+ this.writeDebug('paginationSetup',arguments);
var pagesOutput = '';
- var totalPages = locationset.length / this.settings.locationsPerPage;
var $paginationList = $('.bh-sl-pagination-container .bh-sl-pagination');
+ // Total pages
+ var totalPages = this.totalPages();
+
// Current page check
if (typeof currentPage === 'undefined') {
currentPage = 0;
@@ -709,18 +1412,35 @@
},
/**
- * Marker image setup
- *
+ * Determine if the legacy or Advanced markers should be used
+ *
+ * @returns {boolean}
+ */
+ useLegacyMarkers: function() {
+ this.writeDebug('useLegacyMarkers',arguments);
+
+ return !this.settings.mapSettings.hasOwnProperty('mapId') ||
+ this.settings.mapSettings.mapId === '';
+ },
+
+ /**
+ * Legacy marker image setup
+ *
+ * Original functionality supporting the now deprecated google.maps.Marker
+ * This will eventually be removed in favor of markerImage below.
+ *
* @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) {
+ legacyMarkerImage: function (markerUrl, markerWidth, markerHeight) {
+ this.writeDebug('legacyMarkerImage',arguments);
var markerImg;
-
+
// User defined marker dimensions
- if(typeof markerWidth !== 'undefined' && typeof markerHeight !== 'undefined') {
+ if (typeof markerWidth !== 'undefined' && typeof markerHeight !== 'undefined') {
markerImg = {
url: markerUrl,
size: new google.maps.Size(markerWidth, markerHeight),
@@ -735,7 +1455,41 @@
scaledSize: new google.maps.Size(32, 32)
};
}
-
+
+ return markerImg;
+ },
+
+ /**
+ * Marker image setup
+ *
+ * @param markerUrl {string} path to marker image
+ * @param markerWidth {number} width of marker
+ * @param markerHeight {number} height of marker
+ *
+ * @returns {HTMLImageElement} Image element
+ */
+ markerImage: function (markerUrl, markerWidth, markerHeight) {
+ this.writeDebug('markerImage',arguments);
+
+ // Check if legacy marker image should be used
+ if (this.useLegacyMarkers()) {
+ return this.legacyMarkerImage(markerUrl, markerWidth, markerHeight);
+ }
+
+ var markerImg = document.createElement('img');
+ markerImg.src = markerUrl;
+
+ // User defined marker dimensions
+ if (typeof markerWidth !== 'undefined' && typeof markerHeight !== 'undefined') {
+ markerImg.height = markerHeight;
+ markerImg.width = markerWidth;
+ }
+ // Default marker dimensions: 32px x 32px
+ else {
+ markerImg.height = 32;
+ markerImg.width = 32;
+ }
+
return markerImg;
},
@@ -745,73 +1499,112 @@
* @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 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 = [];
-
- // Remove any spaces from category value
- if(typeof category !== 'undefined' && category.length) {
- category = category.replace(/\s+/g, '');
- }
-
+
// Custom multi-marker image override (different markers for different categories
- if(this.settings.catMarkers !== null) {
- // 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], this.settings.catMarkers[categories[i]][1], this.settings.catMarkers[categories[i]][2]);
+ 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],
+ Number(this.settings.catMarkers[categories[i]][1]),
+ Number(this.settings.catMarkers[categories[i]][2])
+ );
+ }
}
}
- }
- // Single category
- else {
- if(category in this.settings.catMarkers) {
- markerImg = this.markerImage(this.settings.catMarkers[category][0], this.settings.catMarkers[category][1], this.settings.catMarkers[category][2]);
+ // Single category
+ else {
+ if (category in this.settings.catMarkers) {
+ markerImg = this.markerImage(
+ this.settings.catMarkers[category][0],
+ Number(this.settings.catMarkers[category][1]),
+ Number(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);
- }
+ 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
+ );
+ }
}
- // Create the default markers
- if (this.settings.storeLimit === -1 || this.settings.storeLimit > 26 || this.settings.catMarkers !== null || this.settings.markerImg !== null) {
- marker = new google.maps.Marker({
- position : point,
- map : map,
- draggable: false,
- icon: markerImg // Reverts to default marker if nothing is passed
- });
+ // Marker setup
+ if (this.settings.callbackCreateMarker) {
+ // Marker override callback
+ marker = this.settings.callbackCreateMarker.call(this, map, point, letter, category);
}
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
- });
+ // 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))) {
+ if (this.useLegacyMarkers()) {
+ marker = new google.maps.Marker({
+ draggable: false,
+ icon : markerImg, // Reverts to default marker if markerImg not set.
+ map : map,
+ optimized: false,
+ position : point,
+ title : name,
+ });
+ } else {
+ marker = new google.maps.marker.AdvancedMarkerElement({
+ content : markerImg, // Reverts to default marker if markerImg not set.
+ draggable: false,
+ map : map,
+ position : point,
+ title : name,
+ });
+ }
+ }
+ else {
+ // Letter markers
+ if (this.useLegacyMarkers()) {
+ marker = new google.maps.Marker({
+ draggable: false,
+ label : letter,
+ map : map,
+ optimized: false,
+ position : point,
+ title : name,
+ });
+ } else {
+ var letterPin = new google.maps.marker.PinElement({glyph: letter});
+
+ marker = new google.maps.marker.AdvancedMarkerElement({
+ content : letterPin.element,
+ draggable: false,
+ map : map,
+ position : point,
+ title : name,
+ });
+ }
+ }
}
return marker;
@@ -823,37 +1616,60 @@
* @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 (this.useLegacyMarkers()) {
+ this._createLocationVariables(currentMarker.get('id'));
+ } else {
+ this._createLocationVariables(currentMarker.bhslID);
+ }
+
+ var altDistLength,
+ distLength;
+
if (locationData.distance <= 1) {
if (this.settings.lengthUnit === 'km') {
distLength = this.settings.kilometerLang;
+ altDistLength = this.settings.mileLang;
}
else {
distLength = this.settings.mileLang;
+ altDistLength = this.settings.kilometerLang;
}
}
else {
if (this.settings.lengthUnit === 'km') {
distLength = this.settings.kilometersLang;
+ altDistLength = this.settings.milesLang;
}
else {
distLength = this.settings.milesLang;
+ altDistLength = this.settings.kilometersLang;
}
}
+ var markerId;
+
// Set up alpha character
- var markerId = currentMarker.get('id');
+ if (this.useLegacyMarkers()) {
+ markerId = currentMarker.get('id');
+ } else {
+ markerId = currentMarker.bhslID;
+ }
+
// Use dot markers instead of alpha if there are more than 26 locations
- if (this.settings.storeLimit === -1 || this.settings.storeLimit > 26) {
+ 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))) {
+ if (page > 0) {
+ indicator = storeStart + markerId + 1;
+ } else {
indicator = markerId + 1;
- }
- else {
+ }
+ } else {
if (page > 0) {
indicator = String.fromCharCode('A'.charCodeAt(0) + (storeStart + markerId));
}
@@ -865,10 +1681,11 @@
// Define location data
return {
location: [$.extend(locationData, {
- 'markerid': markerId,
- 'marker' : indicator,
- 'length' : distLength,
- 'origin' : addressInput
+ 'markerid' : markerId,
+ 'marker' : indicator,
+ 'altlength': altDistLength,
+ 'length' : distLength,
+ 'origin' : originalOrigin
})]
};
},
@@ -881,17 +1698,47 @@
* @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);
+ $('.' + 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
@@ -899,6 +1746,7 @@
* @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);
@@ -909,52 +1757,130 @@
// Opens the infowindow when list item is clicked
if (location === 'left') {
infowindow.setContent(formattedAddress);
- infowindow.open(marker.get('map'), marker);
+
+ if (this.useLegacyMarkers()) {
+ infowindow.open(marker.get('map'), marker);
+ } else {
+ infowindow.open(marker.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 + ']');
-
- // 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()
+ if (this.useLegacyMarkers()) {
+ 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, locationset[markerId], _this.map);
+ }
+
+ $('.' + _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);
+ }
});
- });
+ } else {
+ marker.addListener('click', function (domEvent, latLng) {
+ infowindow.setContent(formattedAddress);
+ infowindow.open(marker.map, marker);
+
+ // Focus on the list
+ var markerId = marker.bhslID;
+ 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, locationset[markerId], _this.map);
+ }
+
+ $('.' + _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) {
- var _this = this;
- var mappingObj = {};
+ 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) {
- var originAddress = data.address;
- mappingObj.lat = position.coords.latitude;
- mappingObj.lng = position.coords.longitude;
+ 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);
+
+ // Fill in the search box.
+ if (typeof originAddress !== 'undefined') {
+ $('#' + _this.settings.addressID).val(originAddress);
+ }
} else {
// Unable to geocode
_this.notify(_this.settings.addressErrorAlert);
@@ -967,16 +1893,81 @@
*
*/
_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);
+
+ var currentUrl = window.location.href;
+ var url = new URL(currentUrl);
+
+ // Update the page in the URL.
+ url.searchParams.set('bhsl-page', parseInt(newPage) + 1);
+
+ // Update the query string param to match the new value.
+ if (history.pushState) {
+ window.history.pushState({path: url.href}, '', url.href);
+ }
// Page change callback
if (this.settings.callbackPageChange) {
@@ -989,12 +1980,14 @@
/**
* Get the address by marker ID
- *
+ *
* @param markerID {number} location ID
+ *
* @returns {string} formatted address
*/
getAddressByMarker: function(markerID) {
- var formattedAddress = null;
+ 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 + ' '; }
@@ -1002,7 +1995,7 @@
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;
},
@@ -1010,33 +2003,53 @@
* Clear the markers from the map
*/
clearMarkers: function() {
- for (var i = 0; i < locationset.length; i++) {
+ 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);
+ this.settings.callbackDirectionsRequest.call(this, origin, locID, map, locationset[locID]);
}
-
+
var destination = this.getAddressByMarker(locID);
- if(destination) {
+ 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));
@@ -1062,18 +2075,16 @@
* 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.settings.locationList + ' .adp').remove();
- $('.' + this.settings.locationList + ' ul').fadeIn();
-
this.reset();
-
+
if ((olat) && (olng)) {
if (this.countFilters() === 0) {
this.settings.mapSettings.zoom = originalZoom;
@@ -1087,113 +2098,367 @@
$(document).off('click.'+pluginName, '.' + this.settings.locationList + ' .bh-sl-close-icon');
},
+ /**
+ * Handle length unit swap
+ *
+ * @param $lengthSwap
+ */
+ lengthUnitSwap: function($lengthSwap) {
+ this.writeDebug('lengthUnitSwap',arguments);
+
+ if ($lengthSwap.val() === 'alt-distance') {
+ $('.' + this.settings.locationList + ' .loc-alt-dist').show();
+ $('.' + this.settings.locationList + ' .loc-default-dist').hide();
+ } else if ($lengthSwap.val() === 'default-distance') {
+ $('.' + this.settings.locationList + ' .loc-default-dist').show();
+ $('.' + this.settings.locationList + ' .loc-alt-dist').hide();
+ }
+ },
+
/**
* Process the form values and/or query string
*
* @param e {Object} event
*/
processForm: function (e) {
- var _this = this;
- var distance = null;
-
- // Stop the form submission
- if(typeof e !== 'undefined' && e !== null) {
+ this.writeDebug('processForm',arguments);
+ var _this = this,
+ distance = null,
+ geocodeRestrictions = {},
+ $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();
}
- // Get the distance if set
- if (this.settings.maxDistance === true) {
- distance = $('#' + this.settings.maxDistanceID).val();
- }
-
- if(this.settings.querystringParams === true) {
+ // Blur any form field to hide mobile keyboards.
+ $('.' + _this.settings.formContainer +' input, .' + _this.settings.formContainer + ' select').blur();
+ // Query string parameters
+ if (this.settings.querystringParams === true) {
// Check for query string parameters
- if(this.getQueryString(this.settings.addressID) || this.getQueryString(this.settings.searchID)){
+ 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);
+
+ // Max distance field.
+ if (distance && $('#' + this.settings.maxDistanceID + ' option[value=' + distance + ']').length) {
+ $distanceInput.val(distance);
+ }
+
+ // Update zoom if origin coordinates are available and a distance query string value is set.
+ if (addressInput && distance) {
+ _this.settings.mapSettings.zoom = 0;
+ }
+
+ // 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 = $('#' + this.settings.addressID).val();
- searchInput = $('#' + this.settings.searchID).val();
- }
}
else {
// Get the user input and use it
- addressInput = $('#' + this.settings.addressID).val();
- searchInput = $('#' + this.settings.searchID).val();
+ 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();
}
- // Get the region setting if set
- var region = $('#' + this.settings.regionID).val();
+ // Form values callback
+ if (this.settings.callbackFormVals) {
+ this.settings.callbackFormVals.call(this, addressInput, searchInput, distance, region);
+ }
+
+ // Add component restriction if the region has been set.
+ if (typeof region !== 'undefined') {
+ geocodeRestrictions = {
+ country: region
+ };
+ }
- if (addressInput === '' && searchInput === '') {
+ // Component restriction value via callback.
+ if (typeof this.settings.callbackGeocodeRestrictions === 'function') {
+ // Component restriction override callback
+ geocodeRestrictions = this.settings.callbackGeocodeRestrictions.call(this, addressInput, searchInput, distance);
+ }
+
+ if (addressInput === '' && searchInput === '' && this.settings.autoGeocode !== true) {
this._start();
}
- else if(addressInput !== '') {
- 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;
- _this.mapping(mappingObj);
- } else {
- // Unable to geocode
- _this.notify(_this.settings.addressErrorAlert);
- }
- });
+ else if (addressInput !== '') {
+ // Check for existing name search and remove if address input is blank.
+ if (searchInput === '' && filters.hasOwnProperty('name')) {
+ delete filters.name;
+ }
+
+ // 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,
+ componentRestrictions: geocodeRestrictions,
+ 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);
}
- else if(searchInput !== '') {
+ else if (this.settings.autoGeocode === true) {
+ // Run the mapping function
+ mappingObj.lat = olat;
+ mappingObj.lng = olng;
+ mappingObj.origin = addressInput;
mappingObj.name = searchInput;
+ mappingObj.distance = distance;
_this.mapping(mappingObj);
}
+
+ // Reset pagination if the input has changed.
+ if (typeof originalOrigin !== 'undefined' && addressInput !== originalOrigin) {
+ this.paginationReset();
+ }
},
/**
- * Checks distance of each location and setups up the locationset array
- *
+ * 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 firstRun {boolean} initial load check
* @param origin {string} origin address
* @param maxDistance {number} maximum distance if set
*/
- locationsSetup: function (data, lat, lng, firstRun, origin, maxDistance) {
+ 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);
+
+ // Alternative distance length unit
+ if (this.settings.lengthUnit === 'm') {
+ // Miles to kilometers
+ data.altdistance = parseFloat(data.distance)*1.609344;
+ } else if (this.settings.lengthUnit === 'km') {
+ // Kilometers to miles
+ data.altdistance = parseFloat(data.distance)/1.609344;
+ }
}
}
+ // Make sure the location coordinates are valid.
+ if (!this.coordinatesInRange(data.lat, data.lng)) {
+ this.writeDebug('locationsSetup', "location ignored because coordinates out of range: " + maxDistance, data);
+ return;
+ }
+
// Create the array
- if (this.settings.maxDistance === true && firstRun !== true && maxDistance !== null) {
- if (data.distance < maxDistance) {
+ if (this.settings.maxDistance === true && typeof maxDistance !== 'undefined' && maxDistance !== null) {
+ if (data.distance <= maxDistance) {
locationset.push( data );
+ } else {
+ this.writeDebug('locationsSetup', "location ignored because it is out of maxDistance: " + maxDistance, data);
+ return;
}
- else {
+ } else if (this.settings.maxDistance === true && this.settings.querystringParams === true && typeof maxDistance !== 'undefined' && maxDistance !== null) {
+ if (data.distance <= maxDistance) {
+ locationset.push( data );
+ } else {
+ this.writeDebug('locationsSetup', "location ignored because it is out of maxDistance: " + maxDistance, data);
return;
}
- }
- else {
+ } else {
locationset.push( data );
}
},
+ /**
+ * Set up front-end sorting functionality
+ */
+ sorting: function() {
+ this.writeDebug('sorting',arguments);
+ var _this = this,
+ $mapDiv = $('#' + _this.settings.mapID),
+ $sortSelect = $('#' + _this.settings.sortID);
+
+ if ($sortSelect.length === 0) {
+ return;
+ }
+
+ $sortSelect.on('change.'+pluginName, function (e) {
+ e.stopPropagation();
+
+ // Reset pagination.
+ if (_this.settings.pagination === true) {
+ _this.paginationChange(0);
+ }
+
+ var sortMethod,
+ sortVal;
+
+ sortMethod = (typeof $(this).find(':selected').attr('data-method') !== 'undefined') ? $(this).find(':selected').attr('data-method') : 'distance';
+ sortVal = $(this).val();
+
+ _this.settings.sortBy.method = sortMethod;
+ _this.settings.sortBy.prop = sortVal;
+
+ // Callback
+ if (_this.settings.callbackSorting) {
+ _this.settings.callbackSorting.call(this, _this.settings.sortBy);
+ }
+
+ if ($mapDiv.hasClass('bh-sl-map-open')) {
+ _this.mapping(mappingObj);
+ }
+ });
+ },
+
+ /**
+ * Set up front-end ordering functionality - this ties in to sorting and that has to be enabled for this to
+ * work.
+ */
+ order: function() {
+ this.writeDebug('order',arguments);
+ var _this = this,
+ $mapDiv = $('#' + _this.settings.mapID),
+ $orderSelect = $('#' + _this.settings.orderID);
+
+ if ($orderSelect.length === 0) {
+ return;
+ }
+
+ $orderSelect.on('change.'+pluginName, function (e) {
+ e.stopPropagation();
+
+ // Reset pagination.
+ if (_this.settings.pagination === true) {
+ _this.paginationChange(0);
+ }
+
+ _this.settings.sortBy.order = $(this).val();
+
+ // Callback
+ if (_this.settings.callbackOrder) {
+ _this.settings.callbackOrder.call(this, _this.settings.order);
+ }
+
+ if ($mapDiv.hasClass('bh-sl-map-open')) {
+ _this.mapping(mappingObj);
+ }
+ });
+ },
+
+ /**
+ * Distance filtering
+ */
+ distanceFiltering: function () {
+ this.writeDebug('distanceFiltering');
+ var _this = this;
+ var $distanceInput = $('#' + this.settings.maxDistanceID);
+
+ // Add event listener
+ $distanceInput.on('change.'+pluginName, function (e) {
+ e.stopPropagation();
+
+ // Query string parameter value updates on change.
+ if (_this.settings.querystringParams === true) {
+ var currentUrl = window.location.href;
+ var url = new URL(currentUrl);
+
+ // Update the distance in the URL.
+ url.searchParams.set(_this.settings.maxDistanceID, this.value);
+
+ // Update the query string param to match the new value.
+ if (history.pushState) {
+ window.history.pushState({path: url.href}, '', url.href);
+ } else {
+ window.location.replace(url.href);
+ }
+ }
+
+ if ($('#' + _this.settings.mapID).hasClass('bh-sl-map-open') === true) {
+ if ((olat) && (olng)) {
+ _this.settings.mapSettings.zoom = 0;
+ _this.processForm();
+ } else {
+ _this.mapping(mappingObj);
+ }
+ }
+ });
+ },
+
/**
* Count the selected filters
*
* @returns {number}
*/
countFilters: function () {
+ this.writeDebug('countFilters');
var filterCount = 0;
if (!this.isEmptyObject(filters)) {
@@ -1213,6 +2478,7 @@
* @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();
@@ -1231,9 +2497,10 @@
* @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];
@@ -1243,10 +2510,11 @@
/**
* 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();
@@ -1261,29 +2529,96 @@
/**
* Check for existing filter selections
- *
*/
checkFilters: function () {
+ this.writeDebug('checkFilters');
for(var key in this.settings.taxonomyFilters) {
- if(this.settings.taxonomyFilters.hasOwnProperty(key)) {
+
+ 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);
}
}
},
+ /**
+ * Select the indicated values from query string parameters.
+ *
+ * @param taxonomy {string} Current taxonomy.
+ * @param value {array} Query string array values.
+ */
+ selectQueryStringFilters: function( taxonomy, value ) {
+ this.writeDebug('selectQueryStringFilters', arguments);
+
+ var $taxGroupContainer = $('#' + this.settings.taxonomyFilters[taxonomy]);
+
+ // Handle checkboxes.
+ if ( $taxGroupContainer.find('input[type="checkbox"]').length ) {
+
+ for ( var i = 0; i < value.length; i++ ) {
+ $taxGroupContainer.find('input:checkbox[value="' + value[i] + '"]').prop('checked', true);
+ }
+ }
+
+ // Handle select fields.
+ if ( $taxGroupContainer.find('select').length ) {
+ // Only expecting one value for select fields.
+ $taxGroupContainer.find('option[value="' + value[0] + '"]').prop('selected', true);
+ }
+
+ // Handle radio buttons.
+ if ( $taxGroupContainer.find('input[type="radio"]').length ) {
+ // Only one value for radio button.
+ $taxGroupContainer.find('input:radio[value="' + value[0] + '"]').prop('checked', true);
+ }
+ },
+
+ /**
+ * 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);
+
+ // Check for multiple values separated by comma.
+ if ( filterVal.indexOf( ',' ) !== -1 ) {
+ filterVal = filterVal.split( ',' );
+ }
+
+ // Only add the taxonomy id if it doesn't already exist
+ if (typeof filterVal !== 'undefined' && filterVal !== '' && filters[key].indexOf(filterVal) === -1) {
+ if ( Array.isArray( filterVal ) ) {
+ filters[key] = filterVal;
+ } else {
+ filters[key] = [filterVal];
+ }
+ }
+
+ // Select the filters indicated in the query string.
+ if ( filters[key].length ) {
+ this.selectQueryStringFilters( key, filters[key] );
+ }
+ }
+ }
+ },
+
/**
* 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++) {
@@ -1296,17 +2631,31 @@
},
/**
- * Taxonomy filtering
+ * Initialize or reset the filters object to its original state
*/
- taxonomyFiltering: function() {
- var _this = this;
+ taxonomyFiltersInit: function () {
+ this.writeDebug('taxonomyFiltersInit');
// Set up the filters
for(var key in this.settings.taxonomyFilters) {
- if(this.settings.taxonomyFilters.hasOwnProperty(key)) {
+ 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) {
@@ -1314,6 +2663,11 @@
var filterVal, filterContainer, filterKey;
+ // Reset pagination.
+ if (_this.settings.pagination === true) {
+ _this.paginationReset();
+ }
+
// Handle checkbox filters
if ($(this).is('input[type="checkbox"]')) {
// First check for existing selections
@@ -1327,10 +2681,10 @@
// 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) {
+ 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;
@@ -1350,10 +2704,10 @@
if ((olat) && (olng)) {
if (_this.countFilters() === 0) {
_this.settings.mapSettings.zoom = originalZoom;
- }
- else {
+ } else {
_this.settings.mapSettings.zoom = 0;
}
+
_this.processForm();
}
else {
@@ -1381,8 +2735,7 @@
if ((olat) && (olng)) {
_this.settings.mapSettings.zoom = 0;
_this.processForm();
- }
- else {
+ } else {
_this.mapping(mappingObj);
}
}
@@ -1407,460 +2760,1247 @@
},
/**
- * The primary mapping function that runs everything
- *
- * @param mappingObject {Object} all the potential mapping properties - latitude, longitude, origin, name, max distance, page
+ * Updates the location list to reflect the markers that are displayed on the map
+ *
+ * @param markers {Object} Map markers
+ * @param map {Object} Google map
*/
- mapping: function (mappingObject) {
+ checkVisibleMarkers: function(markers, map) {
+ this.writeDebug('checkVisibleMarkers',arguments);
var _this = this;
- var orig_lat, orig_lng, origin, name, maxDistance, page, firstRun, marker, bounds, storeStart, storeNumToShow, myOptions, noResults;
- var i = 0;
- if (!this.isEmptyObject(mappingObject)) {
- orig_lat = mappingObject.lat;
- orig_lng = mappingObject.lng;
- origin = mappingObject.origin;
- name = mappingObject.name;
- maxDistance = mappingObject.distance;
- page = mappingObject.page;
- }
-
- // Enable the visual refresh https://developers.google.com/maps/documentation/javascript/basics#VisualRefresh
- google.maps.visualRefresh = true;
+ var locations, listHtml;
- // Set the initial page to zero if not set
- if (typeof page === 'undefined') {
- page = 0;
- }
-
- // Data request
- if (typeof origin === 'undefined' && this.settings.nameSearch === true) {
- dataRequest = _this._getData();
- }
- else {
- // Setup the origin point
- var 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;
+ // Empty the location list
+ $('.' + this.settings.locationList + ' ul').empty();
+
+ // Set up the new list
+ $(markers).each(function(x, marker){
+ if (_this.useLegacyMarkers()) {
+ 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);
+ }
+ } else {
+ if (map.getBounds().contains(marker.position)) {
+ // 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);
+ }
}
- 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);
+ });
+
+ // 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 address
+ 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);
}
-
- // Check filters here to handle selected filtering after page reload
- if(_this.settings.taxonomyFilters !== null && _this.hasEmptyObjectVals(filters)) {
- _this.checkFilters();
+
+ // Empty the location list
+ locList.empty();
+
+ // Append the no results message
+ noResults = $('' + this.settings.noResultsTitle + '
' + this.settings.noResultsDesc + '').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);
}
- /**
- * Process the location data
- */
- dataRequest.done(function (data) {
- var $mapDiv = $('#' + _this.settings.mapID);
- // Get the length unit
- var distUnit = (_this.settings.lengthUnit === 'km') ? _this.settings.kilometersLang : _this.settings.milesLang;
+ this.map.setCenter(center);
- // Save data and origin separately so we can potentially avoid multiple AJAX requests
- originalData = dataRequest;
- originalOrigin = origin;
-
- // Callback
- if (_this.settings.callbackSuccess) {
- _this.settings.callbackSuccess.call(this);
- }
-
- // Set a variable for fullMapStart so we can detect the first run
- if (_this.settings.fullMapStart === true && $mapDiv.hasClass('bh-sl-map-open') === false) {
- firstRun = true;
- }
- else {
- _this.reset();
- }
+ if (originalZoom) {
+ this.map.setZoom(originalZoom);
+ }
+ },
- $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];
- }
- }
+ /**
+ * 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);
- _this.locationsSetup(locationData, orig_lat, orig_lng, firstRun, origin, maxDistance);
+ if (this.settings.originMarker !== true) {
+ return;
+ }
+
+ var marker,
+ originImg;
- i++;
+ if (typeof origin !== 'undefined') {
+ if (this.useLegacyMarkers()) {
+ 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 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()
+ else {
+ originImg = {
+ url: 'https://mt.googleapis.com/vt/icon/name=icons/spotlight/spotlight-waypoint-a.png'
};
+ }
- _this.locationsSetup(locationData, orig_lat, orig_lng, firstRun, origin, maxDistance);
+ marker = new google.maps.Marker({
+ position : originPoint,
+ map : map,
+ icon : originImg,
+ draggable: false
+ });
+ } else {
+ // Default green origin pin.
+ var defaultOriginPin = new google.maps.marker.PinElement({
+ background : '#39b25e',
+ borderColor: '#177d3d',
+ glyphColor : '#177d3c'
+ });
- i++;
+ marker = new google.maps.marker.AdvancedMarkerElement({
+ content : defaultOriginPin.element,
+ draggable: false,
+ map : map,
+ position : originPoint,
+ title : name,
});
- }
- 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;
- }
+ // Origin image.
+ if (this.settings.originMarkerImg !== null) {
+ originImg = document.createElement('img');
+
+ if (this.settings.originMarkerDim === null) {
+ originImg.src = this.settings.originMarkerImg;
+ } else {
+ originImg = this.markerImage(
+ this.settings.originMarkerImg,
+ this.settings.originMarkerDim.width,
+ this.settings.originMarkerDim.height,
+ );
}
- _this.locationsSetup(locationData, orig_lat, orig_lng, firstRun, origin, maxDistance);
+ marker.content = originImg;
+ }
+ }
+ }
+ },
+
+ /**
+ * Modal window setup
+ */
+ modalWindow: function() {
+ this.writeDebug('modalWindow');
- i++;
- });
+ 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();
}
-
- // 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) {
- var taxFilters = {};
-
- 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(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1") + '\\b)';
- }
- }
- }
- // Filter the data
- if (!_this.isEmptyObject(taxFilters)) {
- locationset = $.grep(locationset, function (val) {
- return _this.filterData(val, taxFilters);
- });
- }
+ });
+ },
+
+ /**
+ * Open and select the location closest to the origin
+ *
+ * @param nearestLoc {Object} Details for the nearest location
+ * @param infowindow {Object} Info window object
+ * @param storeStart {number} Starting point of current page when pagination is enabled
+ * @param page {number} Current page number when pagination is enabled
+ */
+ openNearestLocation: function(nearestLoc, infowindow, storeStart, page) {
+ this.writeDebug('openNearestLocation',arguments);
+
+ if (
+ this.settings.openNearest !== true ||
+ typeof nearestLoc === 'undefined' ||
+ typeof originalOrigin === 'undefined' ||
+ (this.settings.fullMapStart === true && firstRun === true && this.settings.querystringParams === false) ||
+ (this.settings.defaultLoc === true && firstRun === true && this.settings.querystringParams === false)
+ ) {
+ return;
+ }
+
+ var _this = this;
+
+ // Callback
+ if (_this.settings.callbackNearestLoc) {
+ _this.settings.callbackNearestLoc.call(this, _this.map, nearestLoc, infowindow, storeStart, page);
+ }
+
+ var markerId = (nearestLoc.hasOwnProperty('markerid')) ? nearestLoc.markerid : 0;
+ var selectedMarker = markers[markerId];
+
+ _this.createInfowindow(selectedMarker, 'left', infowindow, storeStart, page);
+
+ // Scroll list to selected marker
+ var $container = $('.' + _this.settings.locationList);
+ var $selectedLocation = $('.' + _this.settings.locationList + ' li[data-markerid=' + markerId + ']');
+
+ // Focus on the list
+ $('.' + _this.settings.locationList + ' li').removeClass('list-focus');
+ $selectedLocation.addClass('list-focus');
+
+ $container.animate({
+ scrollTop: $selectedLocation.offset().top - $container.offset().top + $container.scrollTop()
+ });
+ },
+
+ /**
+ * Handle clicks from the location list
+ *
+ * @param map {Object} Google map
+ * @param infowindow {Object} Info window object
+ * @param storeStart {number} Starting point of current page when pagination is enabled
+ * @param page {number} Current page number when pagination is enabled
+ */
+ 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, locationset[markerId], map);
}
- // Handle no results
- if (_this.isEmptyObject(locationset)) {
- // Callback
- if (_this.settings.callbackNoResults) {
- _this.settings.callbackNoResults.call(this);
- }
-
- // Hide the map and locations if they're showing
- if ($mapDiv.hasClass('bh-sl-map-open')) {
- $this.hide();
- }
-
- // Append the no results message
- noResults = $('
' + _this.settings.noResultsTitle + '
' + _this.settings.noResultsDesc + '').hide().fadeIn();
-
- // Setup a no results location
- locationset[0] = {
- 'distance': 0,
- 'lat' : 0,
- 'lng': 0
- };
+ if (_this.useLegacyMarkers()) {
+ map.panTo(selectedMarker.getPosition());
+ } else {
+ map.panTo(selectedMarker.position);
}
- // Sort the multi-dimensional array by distance
- if (typeof origin !== 'undefined') {
- _this.sortNumerically(locationset);
+ var listLoc = 'left';
+ _this.createInfowindow(selectedMarker, listLoc, infowindow, storeStart, page);
+
+ // Custom selected marker override
+ if (_this.settings.selectedMarkerImg !== null) {
+ _this.changeSelectedMarker(selectedMarker);
}
- // Featured locations filtering
- if (_this.settings.featuredLocations === true) {
- // Create array for featured locations
- featuredset = $.grep(locationset, function (val) {
- return val.featured === 'true';
- });
+ // Focus on the list
+ $('.' + _this.settings.locationList + ' li').removeClass('list-focus');
+ $('.' + _this.settings.locationList + ' li[data-markerid=' + markerId + ']').addClass('list-focus');
+ });
- // Create array for normal locations
- normalset = $.grep(locationset, function (val) {
- return val.featured !== 'true';
- });
+ // 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, parseInt(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);
+ });
+ },
+
+ /**
+ * Restrict featured locations from displaying in results by a specific distance
+ *
+ * @returns {Array}
+ */
+ featuredDistanceRestriction: function() {
+ this.writeDebug('featuredDistanceRestriction',arguments);
+ var _this = this;
- // Combine the arrays
- locationset = [];
- locationset = featuredset.concat(normalset);
+ featuredset = $.grep(featuredset, function (val) {
+
+ if (val.hasOwnProperty('distance')) {
+ return parseFloat(val.distance) <= parseFloat(_this.settings.featuredDistance);
}
+ });
- // Check the closest marker
- if (_this.settings.maxDistance === true && firstRun !== true && maxDistance) {
- if (typeof locationset[0] === 'undefined' || locationset[0].distance > maxDistance) {
- _this.notify(_this.settings.distanceErrorAlert + maxDistance + ' ' + distUnit);
- return;
- }
+ return featuredset;
+ },
+
+ /**
+ * Restrict featured locations by distance.
+ *
+ * @returns {Array}
+ */
+ featuredRestrictions: function(mappingObject) {
+ this.writeDebug('featuredRestrictions',arguments);
+
+ if (this.settings.featuredDistance === null) {
+ return featuredset;
+ }
+
+ // Featured locations radius restriction.
+ if (this.settings.featuredDistance !== null) {
+ featuredset = this.featuredDistanceRestriction(mappingObject);
+ }
+
+ return featuredset;
+ },
+
+ /**
+ * 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;
+ }
+
+ paginationPage = page;
+ }
+
+ // Override page if the query string was set.
+ var queryStringPage = _this.getQueryString('bhsl-page');
+ if (queryStringPage !== '') {
+ page = paginationPage = parseInt(queryStringPage) - 1;
+ }
+
+ // Data request
+ if (typeof origin === 'undefined' && this.settings.nameSearch === true) {
+ dataRequest = _this._getData();
+ }
+ else {
+ // Set up 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') && this.settings.pagination !== true) {
+ origin = originalOrigin;
+ dataRequest = originalData;
}
else {
- if (_this.settings.distanceAlert !== -1 && locationset[0].distance > _this.settings.distanceAlert) {
- _this.notify(_this.settings.distanceErrorAlert + _this.settings.distanceAlert + ' ' + distUnit);
- }
+ // 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, mappingObject);
}
+ }
- // Output page numbers if pagination setting is true
- if (_this.settings.pagination === true) {
- _this.paginationSetup(page);
- }
+ // 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);
+ });
+ }
+ },
- // Slide in the map container
- if (_this.settings.slideMap === true) {
- $this.slideDown();
- }
-
- // Set up the modal window
- if (_this.settings.modal === true) {
- // Callback
- if (_this.settings.callbackModalOpen) {
- _this.settings.callbackModalOpen.call(this);
+ /**
+ * Reset disabled form fields
+ */
+ resetDisabledFilterVals: function() {
+ this.writeDebug('resetDisabledFilterVals');
+
+ for (var taxKey in this.settings.taxonomyFilters) {
+ if (this.settings.taxonomyFilters.hasOwnProperty(taxKey)) {
+ for (var x = 0; x < this.settings.taxonomyFilters[taxKey].length; x++) {
+ $('#' + this.settings.taxonomyFilters[taxKey] + ' input,option').each(function () {
+ var disabled = $(this).attr('disabled');
+
+ if (typeof disabled !== 'undefined') {
+ $(this).removeAttr('disabled');
+ }
+ });
}
+ }
+ }
+ },
- // 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();
+ /**
+ * Get available filter values
+ *
+ * @param callback
+ */
+ getAvailableFilters: function(callback) {
+ this.writeDebug('getAvailableFilters');
+ var availableValues = [];
+
+ for (var location in locationset) {
+ if (locationset.hasOwnProperty(location)) {
+ // Loop through the location values.
+ for (var locationKey in locationset[location]) {
+ if (filters.hasOwnProperty(locationKey) && locationset[location][locationKey] !== '') {
+ if (availableValues.hasOwnProperty(locationKey)) {
+ var availableVal = availableValues[locationKey].concat(',', locationset[location][locationKey].replace(', ', ',').trim());
+ availableValues[locationKey] = Array.from(new Set(availableVal.split(','))).toString();
+ } else {
+ availableValues[locationKey] = locationset[location][locationKey].replace(', ', ',').trim();
+ }
}
- });
+ }
}
+ }
- // Avoid error if number of locations is less than the default of 26
- if (_this.settings.storeLimit === -1 || locationset.length < _this.settings.storeLimit) {
- storeNum = locationset.length;
+ // Account for missing filter properties in location set.
+ for (var keyName in filters) {
+ if (!availableValues.hasOwnProperty(keyName)) {
+ availableValues[keyName] = '';
}
- else {
- storeNum = _this.settings.storeLimit;
+ }
+
+ callback(availableValues);
+ },
+
+ /**
+ * Disable input fields that aren't available within the current location set
+ */
+ maybeDisableFilterOptions: function() {
+ this.writeDebug('maybeDisableFilterOptions');
+ var availableValues = [];
+ var _this = this;
+
+ // Initially reset any input/option fields that were previously disabled.
+ this.resetDisabledFilterVals();
+
+ // Loop through current location set to determine what filter values are still available.
+ this.getAvailableFilters( function(values) {
+ availableValues = values;
+
+ // Save the original filter values for reference.
+ if (typeof originalFilterVals === 'undefined') {
+ originalFilterVals = availableValues;
}
- // 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;
+ // Update input and option fields to disabled if they're not available.
+ for (var key in _this.settings.taxonomyFilters) {
+ if (_this.settings.taxonomyFilters.hasOwnProperty(key)) {
+
+ // Loop through the taxonomy filter group items.
+ for (var i = 0; i < _this.settings.taxonomyFilters[key].length; i++) {
+ if (_this.settings.taxonomyFilters.hasOwnProperty(key)) {
+ $('#' + _this.settings.taxonomyFilters[key] + ' input, #' + _this.settings.taxonomyFilters[key] + ' option').each(function () {
+
+ // Initial determination of values that should be disabled.
+ if ($(this).val() !== '' && ! Array.from(new Set(availableValues[key].split(','))).includes($(this).val())) {
+ if (! disabledFilterVals.hasOwnProperty(key)) {
+ disabledFilterVals[key] = [];
+ }
+
+ // Handle select options and radio button values when there is no address input.
+ if (
+ (typeof addressInput === 'undefined' || addressInput === '') &&
+ ($(this).prop('tagName') === 'OPTION' || $(this).prop('type') === 'radio') &&
+ _this.hasSingleGroupFilterVal(filters, key) &&
+ Array.from(new Set(originalFilterVals[key].split(','))).includes($(this).val())
+ ) {
+ return;
+ }
+
+ // Handle select options and radio button values when there is address input.
+ if (
+ (typeof addressInput !== 'undefined' || addressInput !== '') &&
+ ($(this).prop('tagName') === 'OPTION' || $(this).prop('type') === 'radio') &&
+ _this.hasSingleGroupFilterVal(filters, key) &&
+ Array.from(new Set(originalFilterVals[key].split(','))).includes($(this).val()) &&
+ _this.countFilters() === 1
+ ) {
+ return;
+ }
- if( (storeStart + storeNumToShow) > locationset.length ) {
- storeNumToShow = _this.settings.locationsPerPage - ((storeStart + storeNumToShow) - locationset.length);
+ // Keep select options and radio button available values after one filter has been selected.
+ if (
+ ($(this).prop('tagName') === 'OPTION' || $(this).prop('type') === 'radio') &&
+ _this.hasSingleGroupFilterVal(filters, key) &&
+ _this.countFilters() > 1 &&
+ Array.from(new Set(originalFilterVals[key].split(','))).includes($(this).val()) &&
+ ! disabledFilterVals[key].includes($(this).val())
+ ) {
+ return;
+ }
+
+ // Track disabled values.
+ if (
+ disabledFilterVals.hasOwnProperty(key) &&
+ Array.isArray(disabledFilterVals[key]) &&
+ ! disabledFilterVals[key].includes($(this).val())
+ ) {
+ disabledFilterVals[key].push($(this).val());
+ }
+
+ $(this).attr('disabled', true);
+ }
+ });
+ }
+ }
}
-
- locationset = locationset.slice(storeStart, storeStart + storeNumToShow);
- storeNum = locationset.length;
- }
- else {
- storeNumToShow = storeNum;
- storeStart = 0;
}
+ });
+ },
- // Google maps settings
- if ((_this.settings.fullMapStart === true && firstRun === true) || (_this.settings.mapSettings.zoom === 0) || (typeof origin === 'undefined')) {
- myOptions = _this.settings.mapSettings;
- bounds = new google.maps.LatLngBounds();
+ /**
+ * 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, nearestLoc;
+ var taxFilters = {};
+ var $lengthSwap = $('#' + _this.settings.lengthSwapID);
+
+ 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, mappingObject, originPoint, data, page);
+ }
+
+ 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 if (
+ (_this.settings.fullMapStart === true && reload === true) ||
+ (_this.settings.autoGeocode === true && reload === true) ||
+ (_this.settings.defaultLoc === true && reload === true)
+ ) {
+ _this.reset();
+ } 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.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);
+ }
+ 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 = {};
- if (page === 0) {
- _this.settings.mapSettings.center = originPoint;
- myOptions = _this.settings.mapSettings;
+ for (var key in this.attributes) {
+ if (this.attributes.hasOwnProperty(key)) {
+ locationData[this.attributes[key].name] = this.attributes[key].value;
+ }
}
- else {
- _this.settings.mapSettings.center = nextPoint;
- myOptions = _this.settings.mapSettings;
+
+ _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' && '' !== searchInput) {
+
+ if (_this.settings.nameAttribute.indexOf(',')) {
+ nameAttrs = _this.settings.nameAttribute.split(',');
+
+ // Multiple name attributes should swap to exclusive filtering.
+ if (_this.settings.exclusiveTax !== null) {
+ _this.settings.exclusiveTax.concat(nameAttrs);
+ } else {
+ _this.settings.exclusiveTax = nameAttrs;
+ }
+
+ for (var a = 0; a < nameAttrs.length; a++) {
+ filters[nameAttrs[a].trim()] = [searchInput];
+ }
+ } else {
+ filters[_this.settings.nameAttribute] = [searchInput];
}
}
- else {
- _this.settings.mapSettings.center = originPoint;
- myOptions = _this.settings.mapSettings;
+
+ // Check for a previous value.
+ if (
+ typeof searchInput !== 'undefined' &&
+ '' === searchInput
+ ) {
+ if (typeof nameAttrs !== 'undefined') {
+ for (var pa = 0; pa < nameAttrs.length; pa++) {
+ if (nameAttrs[pa] in filters) {
+ delete filters[nameAttrs[pa]];
+ }
+ }
+ } else {
+ delete filters[_this.settings.nameAttribute];
+ }
}
+ }
- // Create the map
- var map = new google.maps.Map(document.getElementById(_this.settings.mapID), myOptions);
+ // Taxonomy filtering setup
+ if (_this.settings.taxonomyFilters !== null || _this.settings.nameSearch === true) {
- // Re-center the map when the browser is resized
- google.maps.event.addDomListener(window, 'resize', function() {
- var center = map.getCenter();
- google.maps.event.trigger(map, 'resize');
- map.setCenter(center);
- });
-
- // Load the map
- $this.data(_this.settings.mapID.replace('#', ''), map);
-
- // Initialize the infowondow
- var infowindow = new google.maps.InfoWindow();
-
- // Add origin marker if the setting is set
- if (_this.settings.originMarker === true) {
- var originImg = '';
-
- // If fullMapStart is on and it's the first run there is no origin
- if(_this.settings.fullMapStart === false && firstRun === true) {
- return;
- }
- else{
- if(_this.settings.originMarkerImg !== null) {
- if(_this.settings.originMarkerDim === null) {
- originImg = _this.markerImage(_this.settings.originMarkerImg);
+ 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] = [];
}
- else {
- originImg = _this.markerImage(_this.settings.originMarkerImg, _this.settings.originMarkerDim.width, _this.settings.originMarkerDim.height);
+
+ // Swap pattern matching depending on name search vs. taxonomy filtering.
+ if (typeof nameAttrs !== 'undefined') {
+ if (nameAttrs.indexOf(k) !== -1) {
+ taxFilters[k][z] = '(?:^|\\s)' + filters[k][z].replace(/([.*+?^=!:${}()|\[\]\/\\]|&\s+)/g, '');
+ } else {
+ taxFilters[k][z] = '(?=.*' + filters[k][z].replace(/([.*+?^=!:${}()|\[\]\/\\]|&\s+)/g, '') + '(?!\\s))';
+ }
+ } else {
+ if (k === _this.settings.nameAttribute) {
+ taxFilters[k][z] = '(?:^|\\s)' + filters[k][z].replace(/([.*+?^=!:${}()|\[\]\/\\]|&\s+)/g, '');
+ } else {
+ taxFilters[k][z] = '(?=.*' + filters[k][z].replace(/([.*+?^=!:${}()|\[\]\/\\]|&\s+)/g, '') + '(?!\\s))';
+ }
}
}
- 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
- });
}
}
- // Handle pagination
- $(document).on('click.'+pluginName, '.bh-sl-pagination li', function () {
- // Run paginationChange
- _this.paginationChange($(this).attr('data-page'));
- });
-
- // Inline directions
- if(_this.settings.inlineDirections === true){
- // 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);
+ // Filter the data
+ if (!_this.isEmptyObject(taxFilters)) {
+ locationset = $.grep(locationset, function (val) {
+ return _this.filterData(val, taxFilters);
});
+ }
+ }
- // Close directions
- $(document).on('click.'+pluginName, '.' + _this.settings.locationList + ' .bh-sl-close-icon', function () {
- _this.closeDirections();
- });
+ // Sorting
+ if (_this.settings.sortBy !== null && typeof _this.settings.sortBy === 'object') {
+
+ // Sort the multi-dimensional array by distance to get the nearest location first when enabled
+ if (_this.settings.openNearest === true && typeof originalOrigin !== 'undefined') {
+ this.sortNumerically(locationset, true);
+
+ // Save the closest location to a variable for openNearest setting
+ if (typeof locationset[0] !== 'undefined') {
+
+ if (this.settings.sortBy.hasOwnProperty('order') && this.settings.sortBy.order.toString() === 'desc') {
+ nearestLoc = locationset[locationset.length - 1];
+ } else {
+ nearestLoc = locationset[0];
+ }
+ }
}
- // Add markers and infowindows loop
- for (var y = 0; y <= storeNumToShow - 1; y++) {
- var letter = '';
+ // Custom sorting
+ _this.sortCustom(locationset);
+ } else {
+ // Sort the multi-dimensional array by distance
+ if (typeof origin !== 'undefined') {
+ _this.sortNumerically(locationset);
+ }
- if (page > 0) {
- letter = String.fromCharCode('A'.charCodeAt(0) + (storeStart + y));
+ // 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 {
- letter = String.fromCharCode('A'.charCodeAt(0) + y);
+ if (typeof locationset[0] !== 'undefined') {
+ if (
+ _this.settings.distanceAlert !== -1 &&
+ locationset[0].distance > _this.settings.distanceAlert &&
+ (typeof paginationPage === 'undefined' || parseInt(paginationPage) === 1)
+ ) {
+ _this.notify(_this.settings.distanceErrorAlert + _this.settings.distanceAlert + ' ' + distUnit);
+ distError = true;
+ }
+ }
+ else {
+ _this.emptyResult();
+ throw new Error('No locations found. Please check the dataLocation setting and path.');
+ return;
+ }
}
-
- var point = new google.maps.LatLng(locationset[y].lat, locationset[y].lng);
- marker = _this.createMarker(point, locationset[y].name, locationset[y].address, letter, 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')) {
- bounds.extend(point);
+ }
+
+ // Save the closest location to a variable for openNearest setting
+ if (typeof locationset[0] !== 'undefined') {
+ nearestLoc = locationset[0];
+ }
+ }
+
+ // Featured locations filtering
+ if (_this.settings.featuredLocations === true) {
+
+ // Create array for featured locations
+ featuredset = $.grep(locationset, function (val) {
+ if (val.hasOwnProperty('featured')) {
+ return val.featured === 'true';
+ }
+ });
+
+ // Featured location restrictions.
+ featuredset = _this.featuredRestrictions(mappingObject);
+
+ // Create array for normal locations
+ normalset = $.grep(locationset, function (val) {
+ if (val.hasOwnProperty('featured')) {
+ return val.featured !== 'true';
}
- // Pass variables to the pop-up infowindows
- _this.createInfowindow(marker, null, infowindow, storeStart, page);
+ });
+
+ // Combine the arrays
+ locationset = [];
+ locationset = featuredset.concat(normalset);
+ }
+
+ // Disable filter inputs if there are no locations with the values left.
+ if (
+ (firstRun !== true && _this.settings.exclusiveFiltering === false) ||
+ (_this.settings.fullMapStart === true && _this.settings.exclusiveFiltering === false) ||
+ (_this.settings.defaultLoc === true && _this.settings.exclusiveFiltering === false)
+ ) {
+ _this.maybeDisableFilterOptions();
+ }
+
+ // Slide in the map container
+ if (_this.settings.slideMap === true) {
+ $this.slideDown();
+ }
+
+ // Output page numbers if pagination setting is true
+ if (_this.settings.pagination === true) {
+ _this.paginationSetup(page);
+ }
+
+ // Alternative method to display no results if locations are too far away instead of all locations.
+ if (_this.settings.altDistanceNoResult === true && nearestLoc.distance > _this.settings.distanceAlert) {
+ _this.emptyResult();
+ return;
+ }
+
+ // Handle no results
+ if (_this.isEmptyObject(locationset) || locationset[0].result === 'none') {
+ _this.emptyResult();
+ return;
+ }
+
+ // 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 fullMapStart is enabled and taxFilters is reset and name search and origin are empty, swap back to the original length.
+ if (
+ _this.settings.fullMapStart === true &&
+ _this.isEmptyObject(taxFilters) &&
+ (searchInput === '' || typeof searchInput === 'undefined') &&
+ (addressInput === '' || typeof addressInput === 'undefined')
+ ) {
+ storeNum = locationset.length;
+ }
+
+ // 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);
}
- // Center and zoom if no origin or zoom was provided
- if ((_this.settings.fullMapStart === true && firstRun === true) || (_this.settings.mapSettings.zoom === 0) || (typeof origin === 'undefined')) {
- map.fitBounds(bounds);
+ 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.querystringParams !== true) ||
+ (_this.settings.mapSettings.zoom === 0) ||
+ (typeof origin === 'undefined') ||
+ (distError === true) ||
+ ((_this.settings.maxDistance === true && firstRun === false) && this.countFilters() > 0)
+ ) {
+ 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
+ window.addEventListener('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(_this.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);
+ }
- // Create the links that focus on the related marker
- var locList = $('.' + _this.settings.locationList + ' ul');
- locList.empty();
- // Check the locationset and continue with the list setup or show no results message
- if(locationset[0].lat === 0 && locationset[0].lng === 0) {
- locList.append(noResults);
+ // Initialize the infowindow
+ 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 a', function (e) {
+ e.preventDefault();
+ // Run paginationChange
+ _this.paginationChange($(this).parent().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 {
- $(markers).each(function (x) {
- var currentMarker = markers[x];
- _this.listSetup(currentMarker, storeStart, page);
- });
+ letter = String.fromCharCode('A'.charCodeAt(0) + y);
}
- // Handle clicks from the list
- $(document).on('click.'+pluginName, '.' + _this.settings.locationList + ' li', function () {
- var markerId = $(this).data('markerid');
- var selectedMarker = markers[markerId];
+ 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);
- // List click callback
- if (_this.settings.callbackListClick) {
- _this.settings.callbackListClick.call(this, markerId, selectedMarker);
- }
+ if (_this.useLegacyMarkers()) {
+ marker.set('id', y);
+ } else {
+ marker.bhslID = y;
+ }
- // Focus on the list
- $('.' + _this.settings.locationList + ' li').removeClass('list-focus');
- $('.' + _this.settings.locationList + ' li[data-markerid=' + markerId + ']').addClass('list-focus');
+ markers[y] = marker;
- 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
- );
+ // Add marker ID to location data
+ if (_this.useLegacyMarkers()) {
+ locationset[y].markerid = marker.get('id');
+ } else {
+ locationset[y].markerid = marker.bhslID;
+ }
+
+ if (this.settings.dataRaw !== null) {
+ for (var l = 0; l < this.settings.dataRaw.length; l++) {
+ if (this.settings.dataRaw[l] && this.settings.dataRaw[l].hasOwnProperty('id') && this.settings.dataRaw[l].id === locationset[y].id) {
+ this.settings.dataRaw[l].markerid = locationset[y].markerid;
+ }
}
- else {
- _this.createInfowindow(selectedMarker, listLoc, infowindow, storeStart, page);
+ }
+
+ if (
+ (_this.settings.fullMapStart === true && firstRun === true && _this.settings.querystringParams !== true) ||
+ (_this.settings.mapSettings.zoom === 0) ||
+ (typeof origin === 'undefined') ||
+ (distError === true) ||
+ ((_this.settings.maxDistance === true && firstRun === false) && this.countFilters() > 0)
+ ) {
+ 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.querystringParams !== true) ||
+ (_this.settings.mapSettings.zoom === 0) ||
+ (typeof origin === 'undefined') ||
+ (distError === true) ||
+ ((_this.settings.maxDistance === true && firstRun === false) && this.countFilters() > 0)
+ ) {
+ _this.map.fitBounds(bounds);
+
+ // Prevent zooming in too far after fitBounds
+ var zoomListener = google.maps.event.addListener(_this.map, 'idle', function() {
+ if (_this.map.getZoom() > 16) {
+ _this.map.setZoom(16);
}
+ google.maps.event.removeListener(zoomListener);
+ });
+ }
+
+ // 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 &&
+ markers.length > _this.settings.fullMapStartListLimit
+ ) {
+ 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);
});
-
- // Prevent bubbling from list content links
- $(document).on('click.'+pluginName, '.' + _this.settings.locationList + ' li a', function (e) {
+ }
+
+ // Length unit swap setup
+ if ($lengthSwap.length) {
+ _this.lengthUnitSwap($lengthSwap);
+
+ $lengthSwap.on('change.'+pluginName, function (e) {
e.stopPropagation();
+ _this.lengthUnitSwap($lengthSwap);
});
+ }
+
+ // Open nearest location.
+ _this.openNearestLocation(nearestLoc, 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);
-
- // Modal ready callback
- if (_this.settings.modal === true && _this.settings.callbackModalReady) {
- _this.settings.callbackModalReady.call(this);
+ // MarkerClusterer setup.
+ if (_this.useLegacyMarkers()) {
+ if ( typeof MarkerClusterer !== 'undefined' && _this.settings.markerCluster !== null ) {
+ var markerCluster = new MarkerClusterer(_this.map, markers, _this.settings.markerCluster);
}
-
- });
+ } else {
+ if ( typeof markerClusterer !== 'undefined' ) {
+ var customClustererParams = _this.settings.markerCluster;
+
+ new markerClusterer.MarkerClusterer({
+ markers,
+ map: _this.map,
+ customClustererParams
+ });
+ }
+ }
+
+ // 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);
+
+ // Fill in form values from query string parameters.
+ if (_this.settings.querystringParams === true) {
+ var $addressInput = $('#' + _this.settings.addressID);
+ var $searchInput = $('#' + _this.settings.searchID);
+
+ // Address field.
+ if (typeof mappingObj !== 'undefined' && mappingObj.hasOwnProperty('origin') && $addressInput.val() === '') {
+ $addressInput.val(mappingObj.origin);
+ }
+
+ // Name search field.
+ if (typeof mappingObj !== 'undefined' && mappingObj.hasOwnProperty('name') && $searchInput.val() === '') {
+ $searchInput.val(mappingObj.name);
+ }
+ }
+
+ // Modal ready callback
+ if (_this.settings.modal === true && _this.settings.callbackModalReady) {
+ _this.settings.callbackModalReady.call(this, mappingObject);
+ }
+
+ // Filters callback
+ if (_this.settings.callbackFilters) {
+ _this.settings.callbackFilters.call(this, filters, mappingObject);
+ }
+ },
+
+ /**
+ * 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);
+ }
}
});
@@ -1908,4 +4048,4 @@
};
-})(jQuery, window, document);
\ No newline at end of file
+})(jQuery, window, document);
diff --git a/dist/assets/js/plugins/storeLocator/jquery.storelocator.min.js b/dist/assets/js/plugins/storeLocator/jquery.storelocator.min.js
index 9498ce2..ba38032 100644
--- a/dist/assets/js/plugins/storeLocator/jquery.storelocator.min.js
+++ b/dist/assets/js/plugins/storeLocator/jquery.storelocator.min.js
@@ -1,5 +1,5 @@
-/*! jQuery Google Maps Store Locator - v2.0.5 - 2015-01-04
+/*! jQuery Google Maps Store Locator - v3.4.1 - 2024-12-20
* http://www.bjornblog.com/web/jquery-store-locator-plugin
-* Copyright (c) 2015 Bjorn Holine; Licensed MIT */
+* Copyright (c) 2024 Bjorn Holine; Licensed MIT */
-!function(a,b,c,d){"use strict";function e(b,c){g=a(b),this.element=b,this.settings=a.extend({},D,c),this._defaults=D,this._name=f,this.init()}var f="storeLocator";if("undefined"==typeof a.fn[f]){var g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v=[],w=[],x=[],y=[],z={},A={},B={},C={},D={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,lengthUnit:"m",storeLimit:26,distanceAlert:60,dataType:"xml",dataLocation:"data/locations.xml",xmlElement:"marker",listColor1:"#ffffff",listColor2:"#eeeeee",originMarker:!1,originMarkerImg:null,originMarkerDim:null,bounceMarker:!0,slideMap:!0,modal:!1,overlay:"bh-sl-overlay",modalWindow:"bh-sl-modal-window",modalContent:"bh-sl-modal-content",closeIcon:"bh-sl-close-icon",defaultLoc:!1,defaultLat:null,defaultLng:null,autoGeocode:!1,maxDistance:!1,maxDistanceID:"bh-sl-maxdistance",fullMapStart:!1,noForm:!1,loading:!1,loadingContainer:"bh-sl-loading",featuredLocations:!1,pagination:!1,locationsPerPage:10,inlineDirections:!1,nameSearch:!1,searchID:"bh-sl-search",nameAttribute:"name",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",querystringParams:!1,callbackNotify:null,callbackBeforeSend:null,callbackSuccess:null,callbackModalOpen:null,callbackModalReady:null,callbackModalClose:null,callbackJsonp:null,callbackPageChange:null,callbackDirectionsRequest:null,callbackCloseDirections:null,callbackNoResults:null,callbackListClick:null,callbackMarkerClick:null,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"};a.extend(e.prototype,{init:function(){B.EarthRadius="km"===this.settings.lengthUnit?6367:3956,j="kml"===this.settings.dataType?"xml":this.settings.dataType,this.settings.inlineDirections===!0&&(t=new google.maps.DirectionsRenderer,u=new google.maps.DirectionsService,a("."+this.settings.locationList).prepend('
')),m=this.settings.mapSettings.zoom,Handlebars.registerHelper("niceURL",function(a){return a?a.replace("https://","").replace("http://",""):void 0}),null!==this.settings.taxonomyFilters&&this.taxonomyFiltering(),this.settings.modal===!0&&(null!==this.settings.taxonomyFilters&&a("."+this.settings.taxonomyFiltersContainer).clone(!0,!0).prependTo(g),g.wrap('
'),a("."+this.settings.modalWindow).prepend('
'),a("."+this.settings.overlay).hide()),this._loadTemplates()},destroy:function(){this.reset();var b=a("#"+this.settings.mapID);if(y.length)for(var d=0;d<=y.length;d++)google.maps.event.removeListener(y[d]);a("."+this.settings.locationList+" ul").empty(),b.hasClass("bh-sl-map-open")&&b.empty().removeClass("bh-sl-map-open"),this.settings.modal===!0&&a(". "+this.settings.overlay).remove(),b.attr("style",""),g.hide(),a.removeData(g.get(0)),a(c).off(f),g.unbind()},reset:function(){w=[],v=[],x=[],y=[],a(c).off("click."+f,"."+this.settings.locationList+" li"),a("."+this.settings.locationList+" .bh-sl-close-directions-container").length&&a(".bh-sl-close-directions-container").remove()},notify:function(a){this.settings.callbackNotify?this.settings.callbackNotify.call(this,a):alert(a)},geoCodeCalcToRadian:function(a){return a*(Math.PI/180)},geoCodeCalcDiffRadian:function(a,b){return this.geoCodeCalcToRadian(b)-this.geoCodeCalcToRadian(a)},geoCodeCalcCalcDistance:function(a,b,c,d,e){return 2*e*Math.asin(Math.min(1,Math.sqrt(Math.pow(Math.sin(this.geoCodeCalcDiffRadian(a,c)/2),2)+Math.cos(this.geoCodeCalcToRadian(a))*Math.cos(this.geoCodeCalcToRadian(c))*Math.pow(Math.sin(this.geoCodeCalcDiffRadian(b,d)/2),2))))},getQueryString:function(a){if(a){a=a.replace(/[\[]/,"\\[").replace(/[\]]/,"\\]");var b=new RegExp("[\\?&]"+a+"=([^]*)"),c=b.exec(location.search);return null===c?"":decodeURIComponent(c[1].replace(/\+/g," "))}},_loadTemplates:function(){var b,c=this,d='
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.
';"kml"===this.settings.dataType&&null===this.settings.listTemplateID&&null===this.settings.infowindowTemplateID?a.when(a.get(this.settings.KMLinfowindowTemplatePath,function(a){b=a,i=Handlebars.compile(b)}),a.get(this.settings.KMLlistTemplatePath,function(a){b=a,h=Handlebars.compile(b)})).then(function(){c.locator()},function(){throw a("."+c.settings.formContainer).append(d),new Error("Could not load storeLocator plugin templates")}):null!==this.settings.listTemplateID&&null!==this.settings.infowindowTemplateID?(i=Handlebars.compile(a("#"+this.settings.infowindowTemplateID).html()),h=Handlebars.compile(a("#"+this.settings.listTemplateID).html()),c.locator()):a.when(a.get(this.settings.infowindowTemplatePath,function(a){b=a,i=Handlebars.compile(b)}),a.get(this.settings.listTemplatePath,function(a){b=a,h=Handlebars.compile(b)})).then(function(){c.locator()},function(){throw a("."+c.settings.formContainer).append(d),new Error("Could not load storeLocator plugin templates")})},locator:function(){this.settings.slideMap===!0&&g.hide(),this._start(),this._formEventHandler()},_formEventHandler:function(){var b=this;this.settings.noForm===!0?(a(c).on("click."+f,"."+this.settings.formContainer+" button",function(a){b.processForm(a)}),a(c).on("keyup."+f,function(c){13===c.keyCode&&a("#"+b.settings.addressID).is(":focus")&&b.processForm(c)})):a(c).on("submit."+f,"#"+this.settings.formID,function(a){b.processForm(a)})},_getData:function(b,c,d){var e=this,f=a.Deferred();return this.settings.callbackBeforeSend&&this.settings.callbackBeforeSend.call(this,b,c,d),this.settings.loading===!0&&a("."+this.settings.formContainer).append('
'),a.ajax({type:"GET",url:this.settings.dataLocation+("jsonp"===this.settings.dataType?(this.settings.dataLocation.match(/\?/)?"&":"?")+"callback=?":""),data:{origLat:b,origLng:c,origAddress:d},dataType:j,jsonpCallback:"jsonp"===this.settings.dataType?this.settings.callbackJsonp:null}).done(function(b){f.resolve(b),e.settings.loading===!0&&a("."+e.settings.formContainer+" ."+e.settings.loadingContainer).remove()}).fail(f.reject),f.promise()},_start:function(){var a=this;if(this.settings.defaultLoc===!0){var b=new this.reverseGoogleGeocode(this),c=new google.maps.LatLng(this.settings.defaultLat,this.settings.defaultLng);b.geocode({latLng:c},function(b){if(null!==b){var c=b.address;C.lat=a.settings.defaultLat,C.lng=a.settings.defaultLng,C.origin=c,a.mapping(C)}else a.notify(a.settings.addressErrorAlert)})}this.settings.fullMapStart===!0&&(this.settings.querystringParams===!0&&this.getQueryString(this.settings.addressID)||this.getQueryString(this.settings.searchID)?this.processForm(null):this.mapping(null)),this.settings.autoGeocode===!0&&navigator.geolocation&&navigator.geolocation.getCurrentPosition(function(b){a.autoGeocodeQuery(b)},function(b){a._autoGeocodeError(b)})},googleGeocode:function(a){var b=new google.maps.Geocoder;this.geocode=function(a,c){b.geocode(a,function(a,b){if(b!==google.maps.GeocoderStatus.OK)throw c(null),new Error("Geocode was not successful for the following reason: "+b);var d={};d.latitude=a[0].geometry.location.lat(),d.longitude=a[0].geometry.location.lng(),c(d)})}},reverseGoogleGeocode:function(a){var b=new google.maps.Geocoder;this.geocode=function(a,c){b.geocode(a,function(a,b){if(b!==google.maps.GeocoderStatus.OK)throw c(null),new Error("Reverse geocode was not successful for the following reason: "+b);if(a[0]){var d={};d.address=a[0].formatted_address,c(d)}})}},roundNumber:function(a,b){return Math.round(a*Math.pow(10,b))/Math.pow(10,b)},isEmptyObject:function(a){for(var b in a)if(a.hasOwnProperty(b))return!1;return!0},hasEmptyObjectVals:function(a){var b=!0;for(var c in a)a.hasOwnProperty(c)&&""!==a[c]&&0!==a[c].length&&(b=!1);return b},modalClose:function(){this.settings.callbackModalClose&&this.settings.callbackModalClose.call(this),z={},a("."+this.settings.overlay+" select").prop("selectedIndex",0),a("."+this.settings.overlay+" input").prop("checked",!1),a("."+this.settings.overlay).hide()},_createLocationVariables:function(a){var b;for(var c in w[a])w[a].hasOwnProperty(c)&&(b=w[a][c],"distance"===c&&(b=this.roundNumber(b,2)),A[c]=b)},sortNumerically:function(a){a.sort(function(a,b){return a.distance
b.distance?1:0})},filterData:function(a,b){var c=!0;for(var d in b)b.hasOwnProperty(d)&&(new RegExp(b[d].join(""),"i").test(a[d])||(c=!1));return c?!0:void 0},_paginationOutput:function(a,b){a=parseFloat(a);var c="",d=a+1,e=a-1;a>0&&(c+=''+this.settings.prevPage+" ");for(var f=0;f'+g+"":''+g+" "}return b>d&&(c+=''+this.settings.nextPage+" "),c},paginationSetup:function(b){var c="",d=w.length/this.settings.locationsPerPage,e=a(".bh-sl-pagination-container .bh-sl-pagination");"undefined"==typeof b&&(b=0),0===e.length?c=this._paginationOutput(b,d):(e.empty(),c=this._paginationOutput(b,d)),e.append(c)},markerImage:function(a,b,c){var d;return d="undefined"!=typeof b&&"undefined"!=typeof c?{url:a,size:new google.maps.Size(b,c),scaledSize:new google.maps.Size(b,c)}:{url:a,size:new google.maps.Size(32,32),scaledSize:new google.maps.Size(32,32)}},createMarker:function(a,b,c,d,e,f){var g,h,i,j=[];if("undefined"!=typeof f&&f.length&&(f=f.replace(/\s+/g,"")),null!==this.settings.catMarkers)if(-1!==f.indexOf(",")){j=f.split(",");for(var k=0;k26||null!==this.settings.catMarkers||null!==this.settings.markerImg?g=new google.maps.Marker({position:a,map:e,draggable:!1,icon:h}):(i={url:"https://mt.googleapis.com/vt/icon/name=icons/spotlight/spotlight-waypoint-b.png&text="+d+"&psize=16&font=fonts/Roboto-Regular.ttf&color=ff333333&ax=44&ay=48"},g=new google.maps.Marker({position:a,map:e,icon:i,draggable:!1})),g},_defineLocationData:function(b,c,d){var e="";this._createLocationVariables(b.get("id"));var f;f=A.distance<=1?"km"===this.settings.lengthUnit?this.settings.kilometerLang:this.settings.mileLang:"km"===this.settings.lengthUnit?this.settings.kilometersLang:this.settings.milesLang;var g=b.get("id");return e=-1===this.settings.storeLimit||this.settings.storeLimit>26?g+1:String.fromCharCode(d>0?"A".charCodeAt(0)+(c+g):"A".charCodeAt(0)+g),{location:[a.extend(A,{markerid:g,marker:e,length:f,origin:p})]}},listSetup:function(b,c,d){var e=this._defineLocationData(b,c,d),f=h(e);a("."+this.settings.locationList+" ul").append(f)},createInfowindow:function(b,c,d,e,f){var g=this,h=this._defineLocationData(b,e,f),j=i(h);"left"===c?(d.setContent(j),d.open(b.get("map"),b)):google.maps.event.addListener(b,"click",function(){d.setContent(j),d.open(b.get("map"),b);var c=b.get("id"),e=a("."+g.settings.locationList+" li[data-markerid="+c+"]");g.settings.callbackMarkerClick&&g.settings.callbackMarkerClick.call(this,b,c,e),a("."+g.settings.locationList+" li").removeClass("list-focus"),e.addClass("list-focus");var f=a("."+g.settings.locationList);f.animate({scrollTop:e.offset().top-f.offset().top+f.scrollTop()})})},autoGeocodeQuery:function(a){var b=this,c={},d=new this.reverseGoogleGeocode(this),e=new google.maps.LatLng(a.coords.latitude,a.coords.longitude);d.geocode({latLng:e},function(d){if(null!==d){var e=d.address;c.lat=a.coords.latitude,c.lng=a.coords.longitude,c.origin=e,b.mapping(c)}else b.notify(b.settings.addressErrorAlert)})},_autoGeocodeError:function(){this.notify(this.settings.autoGeocodeErrorAlert)},paginationChange:function(a){this.settings.callbackPageChange&&this.settings.callbackPageChange.call(this,a),C.page=a,this.mapping(C)},getAddressByMarker:function(a){var b=null;return w[a].address&&(b+=w[a].address+" "),w[a].address2&&(b+=w[a].address2+" "),w[a].city&&(b+=w[a].city+", "),w[a].state&&(b+=w[a].state+" "),w[a].postal&&(b+=w[a].postal+" "),w[a].country&&(b+=w[a].country+" "),b},clearMarkers:function(){for(var a=0;a
')}a(c).off("click","."+this.settings.locationList+" li .loc-directions a")},closeDirections:function(){this.settings.callbackCloseDirections&&this.settings.callbackCloseDirections.call(this),a("."+this.settings.locationList+" .adp").remove(),a("."+this.settings.locationList+" ul").fadeIn(),this.reset(),q&&r&&(this.settings.mapSettings.zoom=0===this.countFilters()?m:0,this.processForm(null)),a(c).off("click."+f,"."+this.settings.locationList+" .bh-sl-close-icon")},processForm:function(b){var c=this,d=null;"undefined"!=typeof b&&null!==b&&b.preventDefault(),this.settings.maxDistance===!0&&(d=a("#"+this.settings.maxDistanceID).val()),this.settings.querystringParams===!0&&(this.getQueryString(this.settings.addressID)||this.getQueryString(this.settings.searchID))?(p=this.getQueryString(this.settings.addressID),o=this.getQueryString(this.settings.searchID)):(p=a("#"+this.settings.addressID).val(),o=a("#"+this.settings.searchID).val());var e=a("#"+this.settings.regionID).val();if(""===p&&""===o)this._start();else if(""!==p){var f=new this.googleGeocode(this);f.geocode({address:p,region:e},function(a){null!==a?(q=a.latitude,r=a.longitude,C.lat=q,C.lng=r,C.origin=p,C.name=o,C.distance=d,c.mapping(C)):c.notify(c.settings.addressErrorAlert)})}else""!==o&&(C.name=o,c.mapping(C))},locationsSetup:function(a,b,c,d,e,f){if("undefined"!=typeof e&&(a.distance||(a.distance=this.geoCodeCalcCalcDistance(b,c,a.lat,a.lng,B.EarthRadius))),this.settings.maxDistance===!0&&d!==!0&&null!==f){if(!(a.distance
-1&&(z[f].splice(g,1),a("#"+b.settings.mapID).hasClass("bh-sl-map-open")===!0&&(q&&r?(b.settings.mapSettings.zoom=0===b.countFilters()?m:0,b.processForm()):b.mapping(C)))}}else(a(this).is("select")||a(this).is('input[type="radio"]'))&&(b.checkFilters(),d=a(this).val(),e=a(this).closest(".bh-sl-filters").attr("id"),f=b.getFilterKey(e),d?f&&(z[f]=[d],a("#"+b.settings.mapID).hasClass("bh-sl-map-open")===!0&&(q&&r?(b.settings.mapSettings.zoom=0,b.processForm()):b.mapping(C))):(f&&(z[f]=[]),b.reset(),q&&r?(b.settings.mapSettings.zoom=m,b.processForm()):b.mapping(C)))})},mapping:function(d){var e,h,i,j,m,p,t,u,A,B,C,D,E,F=this,G=0;if(this.isEmptyObject(d)||(e=d.lat,h=d.lng,i=d.origin,j=d.name,m=d.distance,p=d.page),google.maps.visualRefresh=!0,"undefined"==typeof p&&(p=0),"undefined"==typeof i&&this.settings.nameSearch===!0)n=F._getData();else{var H=new google.maps.LatLng(e,h);"undefined"!=typeof k&&i===k&&"undefined"!=typeof l?(i=k,n=l):n=F._getData(q,r,i)}null!==F.settings.taxonomyFilters&&F.hasEmptyObjectVals(z)&&F.checkFilters(),n.done(function(d){var j=a("#"+F.settings.mapID),q="km"===F.settings.lengthUnit?F.settings.kilometersLang:F.settings.milesLang;if(l=n,k=i,F.settings.callbackSuccess&&F.settings.callbackSuccess.call(this),F.settings.fullMapStart===!0&&j.hasClass("bh-sl-map-open")===!1?t=!0:F.reset(),j.addClass("bh-sl-map-open"),"json"===F.settings.dataType||"jsonp"===F.settings.dataType)for(var r=0;G0)for(var N=0;N'+F.settings.noResultsTitle+'
'+F.settings.noResultsDesc+"").hide().fadeIn(),w[0]={distance:0,lat:0,lng:0}),"undefined"!=typeof i&&F.sortNumerically(w),F.settings.featuredLocations===!0&&(v=a.grep(w,function(a){return"true"===a.featured}),x=a.grep(w,function(a){return"true"!==a.featured}),w=[],w=v.concat(x)),F.settings.maxDistance===!0&&t!==!0&&m){if("undefined"==typeof w[0]||w[0].distance>m)return void F.notify(F.settings.distanceErrorAlert+m+" "+q)}else-1!==F.settings.distanceAlert&&w[0].distance>F.settings.distanceAlert&&F.notify(F.settings.distanceErrorAlert+F.settings.distanceAlert+" "+q);if(F.settings.pagination===!0&&F.paginationSetup(p),F.settings.slideMap===!0&&g.slideDown(),F.settings.modal===!0&&(F.settings.callbackModalOpen&&F.settings.callbackModalOpen.call(this),a("."+F.settings.overlay).fadeIn(),a(c).on("click."+f,"."+F.settings.closeIcon+", ."+F.settings.overlay,function(){F.modalClose()}),a(c).on("click."+f,"."+F.settings.modalWindow,function(a){a.stopPropagation()}),a(c).on("keyup."+f,function(a){27===a.keyCode&&F.modalClose()})),s=-1===F.settings.storeLimit||w.length
w.length&&(C=F.settings.locationsPerPage-(B+C-w.length)),w=w.slice(B,B+C),s=w.length):(C=s,B=0),F.settings.fullMapStart===!0&&t===!0||0===F.settings.mapSettings.zoom||"undefined"==typeof i)D=F.settings.mapSettings,A=new google.maps.LatLngBounds;else if(F.settings.pagination===!0){var O=new google.maps.LatLng(w[0].lat,w[0].lng);0===p?(F.settings.mapSettings.center=H,D=F.settings.mapSettings):(F.settings.mapSettings.center=O,D=F.settings.mapSettings)}else F.settings.mapSettings.center=H,D=F.settings.mapSettings;var P=new google.maps.Map(c.getElementById(F.settings.mapID),D);google.maps.event.addDomListener(b,"resize",function(){var a=P.getCenter();google.maps.event.trigger(P,"resize"),P.setCenter(a)}),g.data(F.settings.mapID.replace("#",""),P);var Q=new google.maps.InfoWindow;if(F.settings.originMarker===!0){var R="";if(F.settings.fullMapStart===!1&&t===!0)return;R=null!==F.settings.originMarkerImg?null===F.settings.originMarkerDim?F.markerImage(F.settings.originMarkerImg):F.markerImage(F.settings.originMarkerImg,F.settings.originMarkerDim.width,F.settings.originMarkerDim.height):{url:"https://mt.googleapis.com/vt/icon/name=icons/spotlight/spotlight-waypoint-a.png"},u=new google.maps.Marker({position:H,map:P,icon:R,draggable:!1})}a(c).on("click."+f,".bh-sl-pagination li",function(){F.paginationChange(a(this).attr("data-page"))}),F.settings.inlineDirections===!0&&(a(c).on("click."+f,"."+F.settings.locationList+" li .loc-directions a",function(b){b.preventDefault();var c=a(this).closest("li").attr("data-markerid");F.directionsRequest(i,c,P)}),a(c).on("click."+f,"."+F.settings.locationList+" .bh-sl-close-icon",function(){F.closeDirections()}));for(var S=0;C-1>=S;S++){var T="";T=String.fromCharCode(p>0?"A".charCodeAt(0)+(B+S):"A".charCodeAt(0)+S);var U=new google.maps.LatLng(w[S].lat,w[S].lng);u=F.createMarker(U,w[S].name,w[S].address,T,P,w[S].category),u.set("id",S),y[S]=u,(F.settings.fullMapStart===!0&&t===!0||0===F.settings.mapSettings.zoom||"undefined"==typeof i)&&A.extend(U),F.createInfowindow(u,null,Q,B,p)}(F.settings.fullMapStart===!0&&t===!0||0===F.settings.mapSettings.zoom||"undefined"==typeof i)&&P.fitBounds(A);var V=a("."+F.settings.locationList+" ul");V.empty(),0===w[0].lat&&0===w[0].lng?V.append(E):a(y).each(function(a){var b=y[a];F.listSetup(b,B,p)}),a(c).on("click."+f,"."+F.settings.locationList+" li",function(){var b=a(this).data("markerid"),c=y[b];F.settings.callbackListClick&&F.settings.callbackListClick.call(this,b,c),a("."+F.settings.locationList+" li").removeClass("list-focus"),a("."+F.settings.locationList+" li[data-markerid="+b+"]").addClass("list-focus"),P.panTo(c.getPosition());var d="left";F.settings.bounceMarker===!0?(c.setAnimation(google.maps.Animation.BOUNCE),setTimeout(function(){c.setAnimation(null),F.createInfowindow(c,d,Q,B,p)},700)):F.createInfowindow(c,d,Q,B,p)}),a(c).on("click."+f,"."+F.settings.locationList+" li a",function(a){a.stopPropagation()}),a("."+F.settings.locationList+" ul li:even").css("background",F.settings.listColor1),a("."+F.settings.locationList+" ul li:odd").css("background",F.settings.listColor2),F.settings.modal===!0&&F.settings.callbackModalReady&&F.settings.callbackModalReady.call(this)})}}),a.fn[f]=function(b){var c=arguments;if(b===d||"object"==typeof b)return this.each(function(){a.data(this,"plugin_"+f)||a.data(this,"plugin_"+f,new e(this,b))});if("string"==typeof b&&"_"!==b[0]&&"init"!==b){var g;return this.each(function(){var d=a.data(this,"plugin_"+f);d instanceof e&&"function"==typeof d[b]&&(g=d[b].apply(d,Array.prototype.slice.call(c,1))),"destroy"===b&&a.data(this,"plugin_"+f,null)}),g!==d?g:this}}}}(jQuery,window,document);
\ No newline at end of file
+!function(B,N,j,t){"use strict";var _,a,i,c,z,Q,V,q,U,K,g,h,H,n,m,f,s,W,J,$,o,Z,X,Y,tt,et,st,r,l,it,u,d,nt="storeLocator",b=!1,e=null;function p(t,s){var i,e;_=B(t),this.element=t,this.settings=B.extend({},d,s),this._defaults=d,this._name=nt,""!==this.settings.mapSettingsID&&(this.settings.mapSettings.mapId=this.settings.mapSettingsID),this.settings.lazyLoadMap&&null!==this.settings.apiKey&&"undefined"==typeof google?(i=this,e={},e.libraries="marker",!0===this.settings.autoComplete&&(e.libraries="places,marker"),this.settings.callbackBeforeMapInject?new Promise(function(t,e){i.settings.callbackBeforeMapInject.call(this,s,t)}).then(function(){i.triggerMapLoad(e)}):i.triggerMapLoad(e)):this.init()}void 0===B.fn[nt]&&(d={ajaxData:null,altDistanceNoResult:!(u={}),apiKey:null,autoComplete:!(it={}),autoCompleteDisableListener:!(l={}),autoCompleteOptions:{},autoGeocode:!(r={}),bounceMarker:!0,catMarkers:null,dataLocation:"data/locations.json",dataRaw:null,dataType:"json",debug:!(st={}),defaultLat:null,defaultLng:null,defaultLoc:!(et=[]),disableAlphaMarkers:!(tt=[]),distanceAlert:60,dragSearch:!(Y=[]),exclusiveFiltering:!(X=[]),exclusiveTax:null,featuredDistance:null,featuredLocations:!1,fullMapStart:!1,fullMapStartBlank:!1,fullMapStartListLimit:!1,infoBubble:null,inlineDirections:!1,lazyLoadMap:!1,lengthUnit:"m",listColor1:"#ffffff",listColor2:"#eeeeee",loading:!1,locationsPerPage:10,mapSettings:{mapTypeId:"roadmap",zoom:12},mapSettingsID:"",markerCluster:null,markerImg:null,markerDim:null,maxDistance:!1,modal:!1,nameAttribute:"name",nameSearch:!1,noForm:!1,openNearest:!1,originMarker:!1,originMarkerDim:null,originMarkerImg:null,pagination:!1,querystringParams:!1,selectedMarkerImg:null,selectedMarkerImgDim:null,sessionStorage:!1,slideMap:!0,sortBy:null,storeLimit:26,taxonomyFilters:null,visibleMarkersList:!1,xmlElement:"marker",addressID:"bh-sl-address",closeIcon:"bh-sl-close-icon",formContainer:"bh-sl-form-container",formID:"bh-sl-user-location",geocodeID:null,lengthSwapID:"bh-sl-length-swap",loadingContainer:"bh-sl-loading",locationList:"bh-sl-loc-list",mapID:"bh-sl-map",maxDistanceID:"bh-sl-maxdistance",modalContent:"bh-sl-modal-content",modalWindow:"bh-sl-modal-window",orderID:"bh-sl-order",overlay:"bh-sl-overlay",regionID:"bh-sl-region",searchID:"bh-sl-search",sortID:"bh-sl-sort",taxonomyFiltersContainer:"bh-sl-filters-container",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,callbackAutoGeoSuccess:null,callbackBeforeMapInject:null,callbackBeforeSend:null,callbackCloseDirections:null,callbackCreateMarker:null,callbackDirectionsRequest:null,callbackFilters:null,callbackFormVals:null,callbackGeocodeRestrictions:null,callbackJsonp:null,callbackListClick:null,callbackMapSet:null,callbackMarkerClick:null,callbackModalClose:null,callbackModalOpen:null,callbackModalReady:null,callbackNearestLoc:null,callbackNoResults:null,callbackNotify:null,callbackOrder:null,callbackPageChange:null,callbackRegion:null,callbackSorting:null,callbackSuccess:null,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 ",kilometerLang:"kilometer",kilometersLang:"kilometers",mileLang:"mile",milesLang:"miles",noResultsTitle:"No results",noResultsDesc:"No locations were found with the given criteria. Please modify your selections or input.",nextPage:"Next »",prevPage:"« Prev"},B.extend(p.prototype,{init:function(){var t,e=this;this.writeDebug("init"),"km"===this.settings.lengthUnit?l.EarthRadius=6367:l.EarthRadius=3956,c="kml"===this.settings.dataType?"xml":this.settings.dataType,!0===this.settings.inlineDirections&&B("."+this.settings.locationList).prepend('
'),V=this.settings.mapSettings.zoom,Handlebars.registerHelper("niceURL",function(t){if(t)return t.replace("https://","").replace("http://","")}),!0===this.settings.maxDistance&&this.distanceFiltering(),null!==this.settings.taxonomyFilters&&this.taxonomyFiltering(),this.sorting(),this.order(),!0===this.settings.modal&&(null!==this.settings.taxonomyFilters&&B("."+this.settings.taxonomyFiltersContainer).clone(!0,!0).prependTo(_),_.wrap(''),B("."+this.settings.modalWindow).prepend('
'),B("."+this.settings.overlay).hide()),!0===this.settings.autoComplete&&(t=j.getElementById(this.settings.addressID),t=new google.maps.places.Autocomplete(t,this.settings.autoCompleteOptions),!0===this.settings.autoComplete)&&!0!==this.settings.autoCompleteDisableListener&&t.addListener("place_changed",function(t){e.processForm(t)}),this._loadTemplates()},triggerMapLoad:function(t){this.writeDebug("triggerMapLoad");var e=this;this.loadMapsAPI(this.settings.apiKey,t).then(function(t){e.map=t,e.init()})},injectGoogleMapsScript:function(e){if(this.writeDebug("injectGoogleMapsScript"),e=void 0!==e?e:{},b)throw new Error("Google Maps API is already loaded.");var t="https://maps.googleapis.com/maps/api/js?"+Object.keys(e).map(t=>encodeURIComponent(t)+"="+encodeURIComponent(e[t])).join("&"),s=j.createElement("script");s.setAttribute("src",t),s.setAttribute("async",""),s.setAttribute("defer",""),j.head.appendChild(s),b=!0},loadMapsAPI:function(s,i){this.writeDebug("loadMapsAPI"),i=void 0!==i?i:{};var n=this;return e=e||new Promise(function(t,e){try{N.onGoogleMapsAPILoaded=t,n.injectGoogleMapsScript({key:s,loading:"async",callback:"onGoogleMapsAPILoaded",...i})}catch(t){e(t)}}).then(function(){N.google.maps})},destroy:function(){this.writeDebug("destroy"),this.reset();var t=B("#"+this.settings.mapID);if(et.length)for(var e=0;e<=et.length;e++)google.maps.event.removeListener(et[e]);B("."+this.settings.locationList+" ul").empty(),t.hasClass("bh-sl-map-open")&&t.empty().removeClass("bh-sl-map-open"),!0===this.settings.modal&&B(". "+this.settings.overlay).remove(),t.attr("style",""),_.hide(),B.removeData(_.get(0)),B(j).off(nt),_.unbind()},reset:function(){var t;this.writeDebug("reset"),Y=[],X=[],tt=[],W=!(et=[]),B(j).off("click."+nt,"."+this.settings.locationList+" li"),B("."+this.settings.locationList+" .bh-sl-close-directions-container").length&&B(".bh-sl-close-directions-container").remove(),!0===this.settings.inlineDirections&&(0<(t=B("."+this.settings.locationList+" .adp")).length&&(t.remove(),B("."+this.settings.locationList+" ul").fadeIn()),B(j).off("click","."+this.settings.locationList+" li .loc-directions a")),!0===this.settings.pagination&&B(j).off("click."+nt,".bh-sl-pagination li a")},formFiltersReset:function(){var t,e;this.writeDebug("formFiltersReset"),null!==this.settings.taxonomyFilters&&(t=B("."+this.settings.taxonomyFiltersContainer+" input"),e=B("."+this.settings.taxonomyFiltersContainer+" select"),"object"==typeof t)&&(t.each(function(){(B(this).is('input[type="checkbox"]')||B(this).is('input[type="radio"]'))&&B(this).prop("checked",!1)}),e.each(function(){B(this).prop("selectedIndex",0)}))},mapReload:function(){this.writeDebug("mapReload"),this.reset(),J=!0,null!==this.settings.taxonomyFilters&&(this.formFiltersReset(),this.resetDisabledFilterVals(),this.taxonomyFiltersInit()),g&&h?(this.settings.mapSettings.zoom=V,this.processForm()):this.mapping(it)},notify:function(t){this.writeDebug("notify",t),this.settings.callbackNotify?this.settings.callbackNotify.call(this,t):alert(t)},geoCodeCalcToRadian:function(t){return this.writeDebug("geoCodeCalcToRadian",t),t*(Math.PI/180)},geoCodeCalcDiffRadian:function(t,e){return this.writeDebug("geoCodeCalcDiffRadian",arguments),this.geoCodeCalcToRadian(e)-this.geoCodeCalcToRadian(t)},geoCodeCalcCalcDistance:function(t,e,s,i,n){return this.writeDebug("geoCodeCalcCalcDistance",arguments),2*n*Math.asin(Math.min(1,Math.sqrt(Math.pow(Math.sin(this.geoCodeCalcDiffRadian(t,s)/2),2)+Math.cos(this.geoCodeCalcToRadian(t))*Math.cos(this.geoCodeCalcToRadian(s))*Math.pow(Math.sin(this.geoCodeCalcDiffRadian(e,i)/2),2))))},inRange:function(t,e,s){return this.writeDebug("inRange",arguments),e=Math.abs(e),isFinite(e)&&t<=e&&e<=s},coordinatesInRange:function(t,e){return this.writeDebug("coordinatesInRange",arguments),this.inRange(-90,t,90)&&this.inRange(-180,e,180)},getQueryString:function(t){if(this.writeDebug("getQueryString",t),t)return t=t.replace(/[\[]/,"\\[").replace(/[\]]/,"\\]"),null===(t=new RegExp("[\\?&]"+t+"=([^]*)").exec(location.search))?"":decodeURIComponent(t[1].replace(/\+/g," "))},getMap:function(){return this.map},_loadTemplates:function(){this.writeDebug("_loadTemplates");var e,t=this,s='
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.
';"kml"===this.settings.dataType&&null===this.settings.listTemplateID&&null===this.settings.infowindowTemplateID?B.when(B.get(this.settings.KMLinfowindowTemplatePath,function(t){e=t,i=Handlebars.compile(e)}),B.get(this.settings.KMLlistTemplatePath,function(t){e=t,a=Handlebars.compile(e)})).then(function(){t.locator()},function(){throw B("."+t.settings.formContainer).append(s),new Error("Could not load storeLocator plugin templates")}):null!==this.settings.listTemplateID&&null!==this.settings.infowindowTemplateID?(i=Handlebars.compile(B("#"+this.settings.infowindowTemplateID).html()),a=Handlebars.compile(B("#"+this.settings.listTemplateID).html()),t.locator()):B.when(B.get(this.settings.infowindowTemplatePath,function(t){e=t,i=Handlebars.compile(e)}),B.get(this.settings.listTemplatePath,function(t){e=t,a=Handlebars.compile(e)})).then(function(){t.locator()},function(){throw B("."+t.settings.formContainer).append(s),new Error("Could not load storeLocator plugin templates")})},locator:function(){this.writeDebug("locator"),!0===this.settings.slideMap&&_.hide(),this._start(),this._formEventHandler()},_formEventHandler:function(){this.writeDebug("_formEventHandler");var e=this;!0===this.settings.noForm?(B(j).on("click."+nt,"."+this.settings.formContainer+" button",function(t){e.processForm(t)}),B(j).on("keydown."+nt,function(t){13===t.keyCode&&B("#"+e.settings.addressID).is(":focus")&&e.processForm(t)})):B(j).on("submit."+nt,"#"+this.settings.formID,function(t){e.processForm(t)}),B(".bh-sl-reset").length&&B("#"+this.settings.mapID).length&&B(j).on("click."+nt,".bh-sl-reset",function(){e.mapReload()}),B("#"+this.settings.addressID).on("change."+nt,function(){o=t,u={},""!==B.trim(B("#"+e.settings.addressID).val())||void 0!==U&&""!==U||null!==e.settings.taxonomyFilters&&!1===e.settings.exclusiveFiltering&&(z=h=g=t,it={},e.resetDisabledFilterVals(),e.taxonomyFiltersInit(),e.mapping(null))})},_getData:function(t,e,s,i,n){this.writeDebug("_getData",arguments);var a,o=this,r="",l="",g="";return void 0!==i&&void 0!==i.geometry.bounds&&(g=i.formatted_address,r=JSON.stringify(i.geometry.bounds.getNorthEast()),l=JSON.stringify(i.geometry.bounds.getSouthWest())),this.settings.callbackBeforeSend&&this.settings.callbackBeforeSend.call(this,t,e,s,g,r,l,n),null!==o.settings.dataRaw?"xml"===c?B.parseXML(o.settings.dataRaw):"json"===c?Array.isArray&&Array.isArray(o.settings.dataRaw)?o.settings.dataRaw:"string"==typeof o.settings.dataRaw?JSON.parse(o.settings.dataRaw):[]:void 0:(a=B.Deferred(),!0===this.settings.loading&&B("."+this.settings.formContainer).append('
'),i={origLat:t,origLng:e,origAddress:s,formattedAddress:g,boundsNorthEast:r,boundsSouthWest:l},null!==this.settings.ajaxData&&"object"==typeof this.settings.ajaxData&&B.extend(i,this.settings.ajaxData),B.ajax({type:"GET",url:this.settings.dataLocation+("jsonp"===this.settings.dataType?(this.settings.dataLocation.match(/\?/)?"&":"?")+"callback=?":""),data:i,dataType:c,jsonpCallback:"jsonp"===this.settings.dataType?this.settings.callbackJsonp:null}).done(function(t){a.resolve(t),!0===o.settings.loading&&B("."+o.settings.formContainer+" ."+o.settings.loadingContainer).remove()}).fail(a.reject),a.promise())},_start:function(){this.writeDebug("_start");var t,e,s=this,i=this.settings.autoGeocode;!1!==s.settings.fullMapStartBlank?(B("#"+s.settings.mapID).addClass("bh-sl-map-open"),(e=s.settings.mapSettings).zoom=s.settings.fullMapStartBlank,t=new google.maps.LatLng(this.settings.defaultLat,this.settings.defaultLng),e.center=t,s.map=new google.maps.Map(j.getElementById(s.settings.mapID),e),N.addEventListener("resize",function(){var t=s.map.getCenter();google.maps.event.trigger(s.map,"resize"),s.map.setCenter(t)}),s.settings.fullMapStartBlank=!1,e.zoom=V):(!0===this.settings.defaultLoc&&this.defaultLocation(),""!==B.trim(B("#"+this.settings.addressID).val())?(s.writeDebug("Using Address Field"),s.processForm(null),i=!1):!0===this.settings.fullMapStart&&!1===this.settings.defaultLoc&&(!0===this.settings.querystringParams&&this.getQueryString(this.settings.addressID)||!0===this.settings.querystringParams&&this.getQueryString(this.settings.searchID)||!0===this.settings.querystringParams&&this.getQueryString(this.settings.maxDistanceID)?(s.writeDebug("Using Query String"),this.processForm(null),i=!1):this.mapping(null))),!0===this.settings.autoGeocode&&!0===i&&(s.writeDebug("Auto Geo"),s.htmlGeocode()),null!==this.settings.autoGeocode&&(s.writeDebug("Button Geo"),B(j).on("click."+nt,"#"+this.settings.geocodeID,function(){s.htmlGeocode()}))},htmlGeocode:function(){this.writeDebug("htmlGeocode",arguments);var e=this;if(!0===e.settings.sessionStorage&&N.sessionStorage&&N.sessionStorage.getItem("myGeo"))return e.writeDebug("Using Session Saved Values for GEO"),e.autoGeocodeQuery(JSON.parse(N.sessionStorage.getItem("myGeo"))),!1;navigator.geolocation&&navigator.geolocation.getCurrentPosition(function(t){e.writeDebug("Current Position Result");t={coords:{latitude:t.coords.latitude,longitude:t.coords.longitude,accuracy:t.coords.accuracy}};!0===e.settings.sessionStorage&&N.sessionStorage&&N.sessionStorage.setItem("myGeo",JSON.stringify(t)),e.settings.callbackAutoGeoSuccess&&e.settings.callbackAutoGeoSuccess.call(this,t),e.autoGeocodeQuery(t)},function(t){e._autoGeocodeError(t)})},googleGeocode:function(t){t.writeDebug("googleGeocode",arguments);var e=new google.maps.Geocoder;this.geocode=function(t,s){e.geocode(t,function(t,e){if(e!==google.maps.GeocoderStatus.OK)throw s(null),new Error("Geocode was not successful for the following reason: "+e);e={};e.latitude=t[0].geometry.location.lat(),e.longitude=t[0].geometry.location.lng(),e.geocodeResult=t[0],s(e)})}},reverseGoogleGeocode:function(t){t.writeDebug("reverseGoogleGeocode",arguments);var e=new google.maps.Geocoder;this.geocode=function(t,s){e.geocode(t,function(t,e){if(e!==google.maps.GeocoderStatus.OK)throw s(null),new Error("Reverse geocode was not successful for the following reason: "+e);t[0]&&((e={}).address=t[0].formatted_address,e.fullResult=t[0],s(e))})}},roundNumber:function(t,e){return this.writeDebug("roundNumber",arguments),Math.round(t*Math.pow(10,e))/Math.pow(10,e)},isEmptyObject:function(t){for(var e in this.writeDebug("isEmptyObject",arguments),t)if(t.hasOwnProperty(e))return!1;return!0},hasEmptyObjectVals:function(t){this.writeDebug("hasEmptyObjectVals",arguments);var e,s=!0;for(e in t)t.hasOwnProperty(e)&&""!==t[e]&&0!==t[e].length&&(s=!1);return s},hasSingleGroupFilterVal:function(t,e){this.writeDebug("hasSingleGroupFilterVal",arguments);t=Object.assign({},t);return!this.hasEmptyObjectVals(t[e])},modalClose:function(){this.writeDebug("modalClose"),this.settings.callbackModalClose&&this.settings.callbackModalClose.call(this),st={},B("."+this.settings.overlay+" select").prop("selectedIndex",0),B("."+this.settings.overlay+" input").prop("checked",!1),B("."+this.settings.overlay).hide()},_createLocationVariables:function(t){var e,s;for(s in this.writeDebug("_createLocationVariables",arguments),r={},Y[t])Y[t].hasOwnProperty(s)&&(e=Y[t][s],"distance"!==s&&"altdistance"!==s||(e=this.roundNumber(e,2)),r[s]=e)},sortAlpha:function(t){this.writeDebug("sortAlpha",arguments);var s=this.settings.sortBy.hasOwnProperty("prop")&&void 0!==this.settings.sortBy.prop?this.settings.sortBy.prop:"name";this.settings.sortBy.hasOwnProperty("order")&&"desc"===this.settings.sortBy.order.toString()?t.sort(function(t,e){return e[s].toLowerCase().localeCompare(t[s].toLowerCase())}):t.sort(function(t,e){return t[s].toLowerCase().localeCompare(e[s].toLowerCase())})},sortDate:function(t){this.writeDebug("sortDate",arguments);var s=this.settings.sortBy.hasOwnProperty("prop")&&void 0!==this.settings.sortBy.prop?this.settings.sortBy.prop:"date";this.settings.sortBy.hasOwnProperty("order")&&"desc"===this.settings.sortBy.order.toString()?t.sort(function(t,e){return new Date(e[s]).getTime()-new Date(t[s]).getTime()}):t.sort(function(t,e){return new Date(t[s]).getTime()-new Date(e[s]).getTime()})},sortNumerically:function(t,e){this.writeDebug("sortNumerically",arguments);var s=null!==this.settings.sortBy&&this.settings.sortBy.hasOwnProperty("prop")&&void 0!==this.settings.sortBy.prop?this.settings.sortBy.prop:"distance";void 0!==e&&!0===e&&(s="distance"),null!==this.settings.sortBy&&this.settings.sortBy.hasOwnProperty("order")&&"desc"===this.settings.sortBy.order.toString()?t.sort(function(t,e){return e[s]
t[s]?1:0}):t.sort(function(t,e){return t[s]e[s]?1:0})},sortCustom:function(t){this.writeDebug("sortCustom",arguments),this.settings.sortBy.hasOwnProperty("method")&&"alpha"===this.settings.sortBy.method.toString()?this.sortAlpha(t):this.settings.sortBy.hasOwnProperty("method")&&"date"===this.settings.sortBy.method.toString()?this.sortDate(t):this.sortNumerically(t)},filterMatching:function(t,e,s){return this.writeDebug("inclusiveFilter",arguments),void 0!==e&&(s=!0===(s=void 0===s||s)?t.join(""):t.join("|"),!!new RegExp(s,"i").test(e.replace(/([.*+?^=!:${}()|\[\]\/\\]|&\s+)/g,"")))},filterData:function(t,e){this.writeDebug("filterData",arguments);var s,i=!0;for(s in e)if(e.hasOwnProperty(s)){for(var n=[],a=0;a')+''+this.settings.prevPage+" "),5<=t+1&&51 … ');for(var g=s;g')+''+c+" ":(i+='')+''+c+" ";return t+o<=e&&5… ')+'')+''+e+" "),i=n')+''+this.settings.nextPage+" ":i},paginationReset:function(){this.writeDebug("paginationReset",arguments);var t=N.location.href,t=new URL(t);t.searchParams.delete("bhsl-page"),history.pushState&&N.history.pushState({path:t.href},"",t.href)},totalPages:function(){return this.writeDebug("totalPages",arguments),!(void 0!==z&&0this.settings.storeLimit)&&(-1===this.settings.storeLimit||Y.length ul").append(e)},changeSelectedMarker:function(t){var e;void 0!==s&&s.setIcon(f),e=null===this.settings.selectedMarkerImgDim?this.markerImage(this.settings.selectedMarkerImg):this.markerImage(this.settings.selectedMarkerImg,this.settings.selectedMarkerImgDim.width,this.settings.selectedMarkerImgDim.height),f=t.icon,t.setIcon(e),s=t},createInfowindow:function(n,t,a,e,s){this.writeDebug("createInfowindow",arguments);var o=this,e=this._defineLocationData(n,e,s),r=i(e);"left"===t?(a.setContent(r),this.useLegacyMarkers()?a.open(n.get("map"),n):a.open(n.map,n)):this.useLegacyMarkers()?google.maps.event.addListener(n,"click",function(){a.setContent(r),a.open(n.get("map"),n);var t=n.get("id"),e=B("."+o.settings.locationList+" li[data-markerid="+t+"]");0
')),B(j).off("click","."+this.settings.locationList+" li .loc-directions a")},closeDirections:function(){this.writeDebug("closeDirections"),this.settings.callbackCloseDirections&&this.settings.callbackCloseDirections.call(this),this.reset(),g&&h&&(0===this.countFilters()?this.settings.mapSettings.zoom=V:this.settings.mapSettings.zoom=0,this.processForm(null)),B(j).off("click."+nt,"."+this.settings.locationList+" .bh-sl-close-icon")},lengthUnitSwap:function(t){this.writeDebug("lengthUnitSwap",arguments),"alt-distance"===t.val()?(B("."+this.settings.locationList+" .loc-alt-dist").show(),B("."+this.settings.locationList+" .loc-default-dist").hide()):"default-distance"===t.val()&&(B("."+this.settings.locationList+" .loc-default-dist").show(),B("."+this.settings.locationList+" .loc-alt-dist").hide())},processForm:function(t){this.writeDebug("processForm",arguments);var e=this,s=null,i={},n=B("#"+this.settings.addressID),a=B("#"+this.settings.searchID),o=B("#"+this.settings.maxDistanceID),r="";null!=t&&t.preventDefault(),B("."+e.settings.formContainer+" input, ."+e.settings.formContainer+" select").blur(),!0===this.settings.querystringParams&&(this.getQueryString(this.settings.addressID)||this.getQueryString(this.settings.searchID)||this.getQueryString(this.settings.maxDistanceID))?(K=this.getQueryString(this.settings.addressID),U=this.getQueryString(this.settings.searchID),(s=this.getQueryString(this.settings.maxDistanceID))&&B("#"+this.settings.maxDistanceID+" option[value="+s+"]").length&&o.val(s),K&&s&&(e.settings.mapSettings.zoom=0),""!==n.val()&&(K=n.val()),""!==a.val()&&(U=a.val()),""!==o.val()&&(s=o.val())):(K=n.val()||"",U=a.val()||"",!0===this.settings.maxDistance&&(s=o.val()||"")),r=this.settings.callbackRegion?this.settings.callbackRegion.call(this,K,U,s):B("#"+this.settings.regionID).val(),this.settings.callbackFormVals&&this.settings.callbackFormVals.call(this,K,U,s,r),void 0!==r&&(i={country:r}),"function"==typeof this.settings.callbackGeocodeRestrictions&&(i=this.settings.callbackGeocodeRestrictions.call(this,K,U,s)),""===K&&""===U&&!0!==this.settings.autoGeocode?this._start():""!==K?(""===U&&st.hasOwnProperty("name")&&delete st.name,void 0!==z&&void 0!==g&&void 0!==h&&K===z?(it.lat=g,it.lng=h,it.origin=K,it.name=U,it.distance=s,e.mapping(it)):new this.googleGeocode(this).geocode({address:K,componentRestrictions:i,region:r},function(t){null!==t?(g=t.latitude,h=t.longitude,it.lat=g,it.lng=h,it.origin=K,it.name=U,it.distance=s,it.geocodeResult=t.geocodeResult,e.mapping(it)):e.notify(e.settings.addressErrorAlert)})):""!==U?(""===K&&delete it.origin,it.name=U,e.mapping(it)):!0===this.settings.autoGeocode&&(it.lat=g,it.lng=h,it.origin=K,it.name=U,it.distance=s,e.mapping(it)),void 0!==z&&K!==z&&this.paginationReset()},locationsSetup:function(t,e,s,i,n){this.writeDebug("locationsSetup",arguments),void 0===i||t.distance||(t.distance=this.geoCodeCalcCalcDistance(e,s,t.lat,t.lng,l.EarthRadius),"m"===this.settings.lengthUnit?t.altdistance=1.609344*parseFloat(t.distance):"km"===this.settings.lengthUnit&&(t.altdistance=parseFloat(t.distance)/1.609344)),this.coordinatesInRange(t.lat,t.lng)?!0===this.settings.maxDistance&&null!=n?t.distance<=n?Y.push(t):this.writeDebug("locationsSetup","location ignored because it is out of maxDistance: "+n,t):!0!==this.settings.maxDistance||!0!==this.settings.querystringParams||null==n||t.distance<=n?Y.push(t):this.writeDebug("locationsSetup","location ignored because it is out of maxDistance: "+n,t):this.writeDebug("locationsSetup","location ignored because coordinates out of range: "+n,t)},sorting:function(){this.writeDebug("sorting",arguments);var s=this,i=B("#"+s.settings.mapID),t=B("#"+s.settings.sortID);0!==t.length&&t.on("change."+nt,function(t){var e;t.stopPropagation(),!0===s.settings.pagination&&s.paginationChange(0),t=void 0!==B(this).find(":selected").attr("data-method")?B(this).find(":selected").attr("data-method"):"distance",e=B(this).val(),s.settings.sortBy.method=t,s.settings.sortBy.prop=e,s.settings.callbackSorting&&s.settings.callbackSorting.call(this,s.settings.sortBy),i.hasClass("bh-sl-map-open")&&s.mapping(it)})},order:function(){this.writeDebug("order",arguments);var e=this,s=B("#"+e.settings.mapID),t=B("#"+e.settings.orderID);0!==t.length&&t.on("change."+nt,function(t){t.stopPropagation(),!0===e.settings.pagination&&e.paginationChange(0),e.settings.sortBy.order=B(this).val(),e.settings.callbackOrder&&e.settings.callbackOrder.call(this,e.settings.order),s.hasClass("bh-sl-map-open")&&e.mapping(it)})},distanceFiltering:function(){this.writeDebug("distanceFiltering");var e=this;B("#"+this.settings.maxDistanceID).on("change."+nt,function(t){t.stopPropagation(),!0===e.settings.querystringParams&&(t=N.location.href,(t=new URL(t)).searchParams.set(e.settings.maxDistanceID,this.value),history.pushState?N.history.pushState({path:t.href},"",t.href):N.location.replace(t.href)),!0===B("#"+e.settings.mapID).hasClass("bh-sl-map-open")&&(g&&h?(e.settings.mapSettings.zoom=0,e.processForm()):e.mapping(it))})},countFilters:function(){this.writeDebug("countFilters");var t=0;if(!this.isEmptyObject(st))for(var e in st)st.hasOwnProperty(e)&&(t+=st[e].length);return t},_existingCheckedFilters:function(e){this.writeDebug("_existingCheckedFilters",arguments),B("#"+this.settings.taxonomyFilters[e]+" input[type=checkbox]").each(function(){var t;B(this).prop("checked")&&void 0!==(t=B(this).val())&&""!==t&&-1===st[e].indexOf(t)&&st[e].push(t)})},_existingSelectedFilters:function(e){this.writeDebug("_existingSelectedFilters",arguments),B("#"+this.settings.taxonomyFilters[e]+" select").each(function(){var t=B(this).val();void 0!==t&&""!==t&&-1===st[e].indexOf(t)&&(st[e]=[t])})},_existingRadioFilters:function(e){this.writeDebug("_existingRadioFilters",arguments),B("#"+this.settings.taxonomyFilters[e]+" input[type=radio]").each(function(){var t;B(this).prop("checked")&&void 0!==(t=B(this).val())&&""!==t&&-1===st[e].indexOf(t)&&(st[e]=[t])})},checkFilters:function(){for(var t in this.writeDebug("checkFilters"),this.settings.taxonomyFilters)this.settings.taxonomyFilters.hasOwnProperty(t)&&(this._existingCheckedFilters(t),this._existingSelectedFilters(t),this._existingRadioFilters(t))},selectQueryStringFilters:function(t,e){this.writeDebug("selectQueryStringFilters",arguments);var s=B("#"+this.settings.taxonomyFilters[t]);if(s.find('input[type="checkbox"]').length)for(var i=0;i
ul").append(i)):s.getBounds().contains(e.position)&&(n.listSetup(e,0,0),i=a(void 0),B("."+n.settings.locationList+" > ul").append(i))}),B("."+this.settings.locationList+" ul li:even").css("background",this.settings.listColor1),B("."+this.settings.locationList+" ul li:odd").css("background",this.settings.listColor2)},dragSearch:function(t){this.writeDebug("dragSearch",arguments);var e=t.getCenter(),s=this,t=(this.settings.mapSettings.zoom=t.getZoom(),g=it.lat=e.lat(),h=it.lng=e.lng(),new this.reverseGoogleGeocode(this)),e=new google.maps.LatLng(it.lat,it.lng);t.geocode({latLng:e},function(t){null!==t?(it.origin=K=t.address,s.mapping(it)):s.notify(s.settings.addressErrorAlert)})},emptyResult:function(){this.writeDebug("emptyResult",arguments);var t=B("."+this.settings.locationList+" ul"),e=this.settings.mapSettings;this.map=new google.maps.Map(j.getElementById(this.settings.mapID),e),this.settings.callbackNoResults&&this.settings.callbackNoResults.call(this,this.map,e),t.empty(),e=B(''+this.settings.noResultsTitle+'
'+this.settings.noResultsDesc+"").hide().fadeIn(),t.append(e),t=g&&h?new google.maps.LatLng(g,h):new google.maps.LatLng(0,0),this.map.setCenter(t),V&&this.map.setZoom(V)},originMarker:function(t,e,s){var i;this.writeDebug("originMarker",arguments),!0===this.settings.originMarker&&void 0!==e&&(this.useLegacyMarkers()?(i=null!==this.settings.originMarkerImg?null===this.settings.originMarkerDim?this.markerImage(this.settings.originMarkerImg):this.markerImage(this.settings.originMarkerImg,this.settings.originMarkerDim.width,this.settings.originMarkerDim.height):{url:"https://mt.googleapis.com/vt/icon/name=icons/spotlight/spotlight-waypoint-a.png"},new google.maps.Marker({position:s,map:t,icon:i,draggable:!1})):(e=new google.maps.marker.PinElement({background:"#39b25e",borderColor:"#177d3d",glyphColor:"#177d3c"}),e=new google.maps.marker.AdvancedMarkerElement({content:e.element,draggable:!1,map:t,position:s,title:name}),null!==this.settings.originMarkerImg&&(i=j.createElement("img"),null===this.settings.originMarkerDim?i.src=this.settings.originMarkerImg:i=this.markerImage(this.settings.originMarkerImg,this.settings.originMarkerDim.width,this.settings.originMarkerDim.height),e.content=i)))},modalWindow:function(){var e;this.writeDebug("modalWindow"),!0===this.settings.modal&&((e=this).settings.callbackModalOpen&&e.settings.callbackModalOpen.call(this),B("."+e.settings.overlay).fadeIn(),B(j).on("click."+nt,"."+e.settings.closeIcon+", ."+e.settings.overlay,function(){e.modalClose()}),B(j).on("click."+nt,"."+e.settings.modalWindow,function(t){t.stopPropagation()}),B(j).on("keyup."+nt,function(t){27===t.keyCode&&e.modalClose()}))},openNearestLocation:function(t,e,s,i){var n,a;this.writeDebug("openNearestLocation",arguments),!0!==this.settings.openNearest||void 0===t||void 0===z||!0===this.settings.fullMapStart&&!0===W&&!1===this.settings.querystringParams||!0===this.settings.defaultLoc&&!0===W&&!1===this.settings.querystringParams||((n=this).settings.callbackNearestLoc&&n.settings.callbackNearestLoc.call(this,n.map,t,e,s,i),t=t.hasOwnProperty("markerid")?t.markerid:0,a=et[t],n.createInfowindow(a,"left",e,s,i),a=B("."+n.settings.locationList),e=B("."+n.settings.locationList+" li[data-markerid="+t+"]"),B("."+n.settings.locationList+" li").removeClass("list-focus"),e.addClass("list-focus"),a.animate({scrollTop:e.offset().top-a.offset().top+a.scrollTop()}))},listClick:function(s,i,n,a){this.writeDebug("listClick",arguments);var o=this;B(j).on("click."+nt,"."+o.settings.locationList+" li",function(){var t=B(this).data("markerid"),e=et[t];o.settings.callbackListClick&&o.settings.callbackListClick.call(this,t,e,Y[t],s),o.useLegacyMarkers()?s.panTo(e.getPosition()):s.panTo(e.position);o.createInfowindow(e,"left",i,n,a),null!==o.settings.selectedMarkerImg&&o.changeSelectedMarker(e),B("."+o.settings.locationList+" li").removeClass("list-focus"),B("."+o.settings.locationList+" li[data-markerid="+t+"]").addClass("list-focus")}),B(j).on("click."+nt,"."+o.settings.locationList+" li a",function(t){t.stopPropagation()})},resultsTotalCount:function(t){this.writeDebug("resultsTotalCount",arguments);var e=B(".bh-sl-total-results");void 0===t||t<=0||0===e.length||e.text(t)},inlineDirections:function(e,s){var i;this.writeDebug("inlineDirections",arguments),!0===this.settings.inlineDirections&&void 0!==s&&(i=this,B(j).on("click."+nt,"."+i.settings.locationList+" li .loc-directions a",function(t){t.preventDefault();t=B(this).closest("li").attr("data-markerid");i.directionsRequest(s,parseInt(t),e),B(j).on("click."+nt,"."+i.settings.locationList+" .bh-sl-close-icon",function(){i.closeDirections()})}))},visibleMarkersList:function(t,e){var s;this.writeDebug("visibleMarkersList",arguments),!0===this.settings.visibleMarkersList&&(s=this,google.maps.event.addListenerOnce(t,"idle",function(){s.checkVisibleMarkers(e,t)}),google.maps.event.addListener(t,"center_changed",function(){s.checkVisibleMarkers(e,t)}),google.maps.event.addListener(t,"zoom_changed",function(){s.checkVisibleMarkers(e,t)}))},featuredDistanceRestriction:function(){this.writeDebug("featuredDistanceRestriction",arguments);var e=this;return X=B.grep(X,function(t){if(t.hasOwnProperty("distance"))return parseFloat(t.distance)<=parseFloat(e.settings.featuredDistance)})},featuredRestrictions:function(t){return this.writeDebug("featuredRestrictions",arguments),X=null!==this.settings.featuredDistance&&null!==this.settings.featuredDistance?this.featuredDistanceRestriction(t):X},mapping:function(e){this.writeDebug("mapping",arguments);var t,s,i,n,a,o,r=this,l=(this.isEmptyObject(e)||(t=e.lat,s=e.lng,i=e.geocodeResult,n=e.origin,o=e.page),!0===r.settings.pagination&&(Z=o=void 0!==o&&z===K?o:0),r.getQueryString("bhsl-page"));""!==l&&(o=Z=parseInt(l)-1),q=void 0===n&&!0===this.settings.nameSearch?r._getData():(a=new google.maps.LatLng(t,s),void 0!==z&&n===z&&void 0!==Q&&!0!==this.settings.pagination?(n=z,Q):r._getData(g,h,n,i,e)),null!==r.settings.taxonomyFilters&&r.hasEmptyObjectVals(st)&&r.checkFilters(),null!==r.settings.dataRaw?r.processData(e,a,q,o):q.done(function(t){r.processData(e,a,t,o)})},resetDisabledFilterVals:function(){for(var t in this.writeDebug("resetDisabledFilterVals"),this.settings.taxonomyFilters)if(this.settings.taxonomyFilters.hasOwnProperty(t))for(var e=0;e
r)&&p.notify(p.settings.distanceErrorAlert+r+" "+v);else{if(void 0===Y[0])throw p.emptyResult(),new Error("No locations found. Please check the dataLocation setting and path.");-1!==p.settings.distanceAlert&&Y[0].distance>p.settings.distanceAlert&&(void 0===Z||1===parseInt(Z))&&(p.notify(p.settings.distanceErrorAlert+p.settings.distanceAlert+" "+v),h=!0)}void 0!==Y[0]&&(d=Y[0])}if(!0===p.settings.featuredLocations&&(X=B.grep(Y,function(t){if(t.hasOwnProperty("featured"))return"true"===t.featured}),X=p.featuredRestrictions(t),tt=B.grep(Y,function(t){if(t.hasOwnProperty("featured"))return"true"!==t.featured}),Y=[],Y=X.concat(tt)),(!0!==W&&!1===p.settings.exclusiveFiltering||!0===p.settings.fullMapStart&&!1===p.settings.exclusiveFiltering||!0===p.settings.defaultLoc&&!1===p.settings.exclusiveFiltering)&&p.maybeDisableFilterOptions(),!0===p.settings.slideMap&&_.slideDown(),!0===p.settings.pagination&&p.paginationSetup(i),!0===p.settings.altDistanceNoResult&&d.distance>p.settings.distanceAlert)p.emptyResult();else if(p.isEmptyObject(Y)||"none"===Y[0].result)p.emptyResult();else{p.modalWindow(),H=-1===p.settings.storeLimit||Y.lengthY.length&&(c=p.settings.locationsPerPage-(g+c-Y.length)),Y=Y.slice(g,g+c),H=Y.length):(c=H,g=0),p.resultsTotalCount(Y.length),!0===p.settings.fullMapStart&&!0===W&&!0!==p.settings.querystringParams||0===p.settings.mapSettings.zoom||void 0===o||!0===h||!0===p.settings.maxDistance&&!1===W&&0p.settings.fullMapStartListLimit)for(var G=0;G li:even").css("background",p.settings.listColor1),B("."+p.settings.locationList+" ul > li:odd").css("background",p.settings.listColor2),p.visibleMarkersList(p.map,et),!0===p.settings.querystringParams&&(F=B("#"+p.settings.addressID),P=B("#"+p.settings.searchID),void 0!==it&&it.hasOwnProperty("origin")&&""===F.val()&&F.val(it.origin),void 0!==it)&&it.hasOwnProperty("name")&&""===P.val()&&P.val(it.name),!0===p.settings.modal&&p.settings.callbackModalReady&&p.settings.callbackModalReady.call(this,t),p.settings.callbackFilters&&p.settings.callbackFilters.call(this,st,t)}},writeDebug:function(){N.console&&this.settings.debug&&(Function.prototype.bind?this.writeDebug=Function.prototype.bind.call(console.log,console,"StoreLocator :"):this.writeDebug=function(){arguments[0]="StoreLocator : "+arguments[0],Function.prototype.apply.call(console.log,console,arguments)},this.writeDebug.apply(this,arguments))}}),B.fn[nt]=function(e){var s,i=arguments;return e===t||"object"==typeof e?this.each(function(){B.data(this,"plugin_"+nt)||B.data(this,"plugin_"+nt,new p(this,e))}):"string"==typeof e&&"_"!==e[0]&&"init"!==e?(this.each(function(){var t=B.data(this,"plugin_"+nt);t instanceof p&&"function"==typeof t[e]&&(s=t[e].apply(t,Array.prototype.slice.call(i,1))),"destroy"===e&&B.data(this,"plugin_"+nt,null)}),s!==t?s:this):void 0})}(jQuery,window,document);
\ No newline at end of file
diff --git a/dist/assets/js/plugins/storeLocator/templates/infowindow-description.html b/dist/assets/js/plugins/storeLocator/templates/infowindow-description.html
index 786387d..5981a26 100644
--- a/dist/assets/js/plugins/storeLocator/templates/infowindow-description.html
+++ b/dist/assets/js/plugins/storeLocator/templates/infowindow-description.html
@@ -1,11 +1,21 @@
{{#location}}
{{name}}
{{address}}
-{{address2}}
-{{city}}{{#if state}},{{/if}} {{state}} {{postal}}
-{{hours1}}
-{{hours2}}
-{{hours3}}
-{{phone}}
+{{#if address2}}
+ {{address2}}
+{{/if}}
+{{city}}{{#if city}},{{/if}} {{state}} {{postal}}
+{{#if hours1}}
+ {{hours1}}
+{{/if}}
+{{#if hours2}}
+ {{hours2}}
+{{/if}}
+{{#if hours3}}
+ {{hours3}}
+{{/if}}
+{{#if phone}}
+ {{phone}}
+{{/if}}
-{{/location}}
\ No newline at end of file
+{{/location}}
diff --git a/dist/assets/js/plugins/storeLocator/templates/kml-infowindow-description.html b/dist/assets/js/plugins/storeLocator/templates/kml-infowindow-description.html
index bf408a2..afababf 100644
--- a/dist/assets/js/plugins/storeLocator/templates/kml-infowindow-description.html
+++ b/dist/assets/js/plugins/storeLocator/templates/kml-infowindow-description.html
@@ -1,4 +1,6 @@
{{#location}}
{{name}}
-{{{description}}}
-{{/location}}
\ No newline at end of file
+{{#if description}}
+ {{{description}}}
+{{/if}}
+{{/location}}
diff --git a/dist/assets/js/plugins/storeLocator/templates/kml-location-list-description.html b/dist/assets/js/plugins/storeLocator/templates/kml-location-list-description.html
index 17fe9bd..aeaa7ec 100644
--- a/dist/assets/js/plugins/storeLocator/templates/kml-location-list-description.html
+++ b/dist/assets/js/plugins/storeLocator/templates/kml-location-list-description.html
@@ -4,8 +4,10 @@
{{name}}
-
{{{description}}}
+ {{#if description}}
+
{{{description}}}
+ {{/if}}
-{{/location}}
\ No newline at end of file
+{{/location}}
diff --git a/dist/assets/js/plugins/storeLocator/templates/location-list-description.html b/dist/assets/js/plugins/storeLocator/templates/location-list-description.html
index 44c7f15..ab1f455 100644
--- a/dist/assets/js/plugins/storeLocator/templates/location-list-description.html
+++ b/dist/assets/js/plugins/storeLocator/templates/location-list-description.html
@@ -4,14 +4,23 @@
{{name}}
-
{{address}}
-
{{address2}}
-
{{city}}{{#if state}},{{/if}} {{state}} {{postal}}
-
{{phone}}
-
- {{#if distance}}
{{distance}} {{length}}
-
{{/if}}
+
{{address}}
+ {{#if address2}}
+
{{address2}}
+ {{/if}}
+
{{city}}{{#if city}},{{/if}} {{state}} {{postal}}
+ {{#if phone}}
+
{{phone}}
+ {{/if}}
+ {{#if web}}
+
+ {{/if}}
+ {{#if distance}}
+
{{distance}} {{length}}
+ {{#if altdistance}}
{{altdistance}} {{altlength}}
{{/if}}
+
+ {{/if}}
-{{/location}}
\ No newline at end of file
+{{/location}}
diff --git a/dist/autocomplete-example.html b/dist/autocomplete-example.html
new file mode 100644
index 0000000..d94d30f
--- /dev/null
+++ b/dist/autocomplete-example.html
@@ -0,0 +1,52 @@
+
+
+
+ Autocomplete Example
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dist/autogeocode-example.html b/dist/autogeocode-example.html
index 853a15b..3249ebc 100755
--- a/dist/autogeocode-example.html
+++ b/dist/autogeocode-example.html
@@ -1,5 +1,5 @@
-
+
Map Example - Auto geocoding
@@ -7,42 +7,46 @@
-
+
-
+
-
-
+
-
+
-
\ No newline at end of file
+
diff --git a/dist/bootstrap-example.html b/dist/bootstrap-example.html
index 10caad3..0a2c3c4 100644
--- a/dist/bootstrap-example.html
+++ b/dist/bootstrap-example.html
@@ -1,24 +1,27 @@
-
+
Map Example
-
-
+
+
-
-
+
+
+
-
Using Chipotle as an Example
-
I used locations around Minneapolis and the southwest suburbs. So, for example, Edina, Plymouth, Eden
+
Using Chipotle as an Example
+
I used locations around Minneapolis and the southwest suburbs. So, for example, Edina, Plymouth, Eden
Prarie, etc. would be good for testing the functionality. You can use just the city as the address - ex:
Edina, MN.
+
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
+
-
\ No newline at end of file
+
diff --git a/dist/categories-example.html b/dist/categories-example.html
index c946b4a..36a5d03 100644
--- a/dist/categories-example.html
+++ b/dist/categories-example.html
@@ -1,5 +1,5 @@
-
+
Map Example - Categories
@@ -10,7 +10,7 @@
@@ -28,7 +28,7 @@
Using Chipotle as an Example
-
Submit
+
Search
@@ -91,20 +96,35 @@ Using Chipotle as an Example
-
+
+
+ State
+
+
+ All states
+ Minnesota
+ Wisconsin
+
+
+
+
@@ -112,31 +132,35 @@
Using Chipotle as an Example
-
-
+
-
+
-
\ No newline at end of file
+
diff --git a/dist/category-markers-example.html b/dist/category-markers-example.html
index 6d4bd95..306518c 100644
--- a/dist/category-markers-example.html
+++ b/dist/category-markers-example.html
@@ -1,5 +1,5 @@
-
+
Map Example - Category Markers
@@ -10,10 +10,9 @@
@@ -23,32 +22,34 @@
Using Chipotle as an Example
-
Submit
+
Search
-
-
+
-
+
-
\ No newline at end of file
+
diff --git a/dist/cluster-example.html b/dist/cluster-example.html
new file mode 100644
index 0000000..5df6df5
--- /dev/null
+++ b/dist/cluster-example.html
@@ -0,0 +1,53 @@
+
+
+
+ Cluster Example
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dist/data/locations.json b/dist/data/locations.json
index dc97572..8eda7cd 100755
--- a/dist/data/locations.json
+++ b/dist/data/locations.json
@@ -1 +1,422 @@
-[{"id":"1","name":"Chipotle Minneapolis","lat":"44.947464","lng":"-93.320826","address":"3040 Excelsior Blvd","address2":"","city":"Minneapolis","state":"MN","postal":"55416","phone":"612-922-6662","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"2","name":"Chipotle St. Louis Park","lat":"44.930810","lng":"-93.347877","address":"5480 Excelsior Blvd.","address2":"","city":"St. Louis Park","state":"MN","postal":"55416","phone":"952-922-1970","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"3","name":"Chipotle Minneapolis","lat":"44.9553438","lng":"-93.29719699999998","address":"2600 Hennepin Ave.","address2":"","city":"Minneapolis","state":"MN","postal":"55404","phone":"612-377-6035","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"4","name":"Chipotle Golden Valley","lat":"44.983935","lng":"-93.380542","address":"515 Winnetka Ave. N","address2":"","city":"Golden Valley","state":"MN","postal":"55427","phone":"763-544-2530","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"5","name":"Chipotle Hopkins","lat":"44.924363","lng":"-93.410158","address":"786 Mainstreet","address2":"","city":"Hopkins","state":"MN","postal":"55343","phone":"952-935-0044","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"6","name":"Chipotle Minneapolis","lat":"44.973557","lng":"-93.275111","address":"1040 Nicollet Ave","address2":"","city":"Minneapolis","state":"MN","postal":"55403","phone":"612-659-7955","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"7","name":"Chipotle Minneapolis","lat":"44.97774","lng":"-93.270909","address":"50 South 6th","address2":"","city":"Minneapolis","state":"MN","postal":"55402","phone":"612-333-0434","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"8","name":"Chipotle Edina","lat":"44.879826","lng":"-93.321280","address":"6801 York Avenue South","address2":"","city":"Edina","state":"MN","postal":"55435","phone":"952-926-6651","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"9","name":"Chipotle Minnetonka","lat":"44.970495","lng":"-93.437430","address":"12509 Wayzata Blvd","address2":"","city":"Minnetonka","state":"MN","postal":"55305","phone":"952-252-4900","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"10","name":"Chipotle Minneapolis","lat":"44.972808","lng":"-93.247153","address":"229 Cedar Ave S","address2":"","city":"Minneapolis","state":"MN","postal":"55454","phone":"612-659-7830","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"11","name":"Chipotle Minneapolis","lat":"44.987687","lng":"-93.257581","address":"225 Hennepin Ave E","address2":"","city":"Minneapolis","state":"MN","postal":"55414","phone":"612-331-6330","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"12","name":"Chipotle Minneapolis","lat":"44.973665","lng":"-93.227023","address":"800 Washington Ave SE","address2":"","city":"Minneapolis","state":"MN","postal":"55414","phone":"612-378-7078","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"13","name":"Chipotle Bloomington","lat":"44.8458631","lng":"-93.2860161","address":"322 South Ave","address2":"","city":"Bloomington","state":"MN","postal":"55425","phone":"952-252-3800","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"14","name":"Chipotle Wayzata","lat":"44.9716626","lng":"-93.4967757","address":"1313 Wayzata Blvd","address2":"","city":"Wayzata","state":"MN","postal":"55391","phone":"952-473-7100","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"15","name":"Chipotle Eden Prairie","lat":"44.859761","lng":"-93.436379","address":"13250 Technology Dr","address2":"","city":"Eden Prairie","state":"MN","postal":"55344","phone":"952-934-5955","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"16","name":"Chipotle Plymouth","lat":"45.019846","lng":"-93.481832","address":"3425 Vicksburg Lane N","address2":"","city":"Plymouth","state":"MN","postal":"55447","phone":"763-519-0063","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"17","name":"Chipotle Roseville","lat":"44.998965","lng":"-93.194622","address":"860 Rosedale Center Plaza","address2":"","city":"Roseville","state":"MN","postal":"55113","phone":"651-633-2300","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"18","name":"Chipotle St. Paul","lat":"44.939865","lng":"-93.136768","address":"867 Grand Ave","address2":"","city":"St. Paul","state":"MN","postal":"55105","phone":"651-602-0560","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"19","name":"Chipotle Chanhassen","lat":"44.858736","lng":"-93.533661","address":"560 W 79th","address2":"","city":"Chanhassen","state":"MN","postal":"55317","phone":"952-294-0301","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""},{"id":"20","name":"Chipotle St. Paul","lat":"44.945127","lng":"-93.095368","address":"29 5th St West","address2":"","city":"St. Paul","state":"MN","postal":"55102","phone":"651-291-5411","web":"http:\/\/www.chipotle.com","hours1":"Mon-Sun 11am-10pm","hours2":"","hours3":""}]
\ No newline at end of file
+[
+ {
+ "id": "1",
+ "name": "Chipotle Minneapolis",
+ "lat": "44.947464",
+ "lng": "-93.320826",
+ "category": "Restaurant",
+ "address": "3040 Excelsior Blvd",
+ "address2": "",
+ "city": "Minneapolis",
+ "state": "MN",
+ "postal": "55416",
+ "phone": "612-922-6662",
+ "web": "http:\/\/www.chipotle.com",
+ "hours1": "Mon-Sun 11am-10pm",
+ "hours2": "",
+ "hours3": "",
+ "featured": "",
+ "features": "",
+ "date": "10/17/18"
+ },
+ {
+ "id": "2",
+ "name": "Chipotle St. Louis Park",
+ "lat": "44.930810",
+ "lng": "-93.347877",
+ "category": "Restaurant",
+ "address": "5480 Excelsior Blvd.",
+ "address2": "",
+ "city": "St. Louis Park",
+ "state": "MN",
+ "postal": "55416",
+ "phone": "952-922-1970",
+ "web": "http:\/\/www.chipotle.com",
+ "hours1": "Mon-Sun 11am-10pm",
+ "hours2": "",
+ "hours3": "",
+ "featured": "",
+ "features": "Online Ordering",
+ "date": "09/17/18"
+ },
+ {
+ "id": "3",
+ "name": "Chipotle Minneapolis",
+ "lat": "44.9553438",
+ "lng": "-93.29719699999998",
+ "category": "Restaurant, Bar",
+ "address": "2600 Hennepin Ave.",
+ "address2": "",
+ "city": "Minneapolis",
+ "state": "MN",
+ "postal": "55404",
+ "phone": "612-377-6035",
+ "web": "http:\/\/www.chipotle.com",
+ "hours1": "Mon-Sun 11am-10pm",
+ "hours2": "",
+ "hours3": "",
+ "featured": "true",
+ "features": "Online Ordering, Fresh Guacamole",
+ "date": "08/17/18"
+ },
+ {
+ "id": "4",
+ "name": "Chipotle Golden Valley",
+ "lat": "44.983935",
+ "lng": "-93.380542",
+ "category": "Restaurant, Something & Sons",
+ "address": "515 Winnetka Ave. N",
+ "address2": "",
+ "city": "Golden Valley",
+ "state": "MN",
+ "postal": "55427",
+ "phone": "763-544-2530",
+ "web": "http:\/\/www.chipotle.com",
+ "hours1": "Mon-Sun 11am-10pm",
+ "hours2": "",
+ "hours3": "",
+ "featured": "",
+ "features": "Online Ordering",
+ "date": "07/17/18"
+ },
+ {
+ "id": "5",
+ "name": "Chipotle Hopkins",
+ "lat": "44.924363",
+ "lng": "-93.410158",
+ "category": "Cafe",
+ "address": "786 Mainstreet",
+ "address2": "",
+ "city": "Hopkins",
+ "state": "MN",
+ "postal": "55343",
+ "phone": "952-935-0044",
+ "web": "http:\/\/www.chipotle.com",
+ "hours1": "Mon-Sun 11am-10pm",
+ "hours2": "",
+ "hours3": "",
+ "featured": "",
+ "features": "Margaritas",
+ "date": "06/17/18"
+ },
+ {
+ "id": "6",
+ "name": "Chipotle Minneapolis",
+ "lat": "44.973557",
+ "lng": "-93.275111",
+ "category": "Restaurant",
+ "address": "1040 Nicollet Ave",
+ "address2": "",
+ "city": "Minneapolis",
+ "state": "MN",
+ "postal": "55403",
+ "phone": "612-659-7955",
+ "web": "http:\/\/www.chipotle.com",
+ "hours1": "Mon-Sun 11am-10pm",
+ "hours2": "",
+ "hours3": "",
+ "featured": "",
+ "features": "Online Ordering, Margaritas",
+ "date": "05/17/18"
+ },
+ {
+ "id": "7",
+ "name": "Chipotle Minneapolis",
+ "lat": "44.97774",
+ "lng": "-93.270909",
+ "category": "Restaurant, Cafe",
+ "address": "50 South 6th",
+ "address2": "",
+ "city": "Minneapolis",
+ "state": "MN",
+ "postal": "55402",
+ "phone": "612-333-0434",
+ "web": "http:\/\/www.chipotle.com",
+ "hours1": "Mon-Sun 11am-10pm",
+ "hours2": "",
+ "hours3": "",
+ "featured": "",
+ "features": "Fresh Guacamole",
+ "date": "04/17/18"
+ },
+ {
+ "id": "8",
+ "name": "Chipotle Edina",
+ "lat": "44.879826",
+ "lng": "-93.321280",
+ "category": "Restaurant",
+ "address": "6801 York Avenue South",
+ "address2": "",
+ "city": "Edina",
+ "state": "MN",
+ "postal": "55435",
+ "phone": "952-926-6651",
+ "web": "http:\/\/www.chipotle.com",
+ "hours1": "Mon-Sun 11am-10pm",
+ "hours2": "",
+ "hours3": "",
+ "featured": "",
+ "features": "Online Ordering, Fresh Guacamole",
+ "date": "03/17/18"
+ },
+ {
+ "id": "9",
+ "name": "Chipotle Minnetonka",
+ "lat": "44.970495",
+ "lng": "-93.437430",
+ "category": "Restaurant",
+ "address": "12509 Wayzata Blvd",
+ "address2": "",
+ "city": "Minnetonka",
+ "state": "MN",
+ "postal": "55305",
+ "phone": "952-252-4900",
+ "web": "http:\/\/www.chipotle.com",
+ "hours1": "Mon-Sun 11am-10pm",
+ "hours2": "",
+ "hours3": "",
+ "featured": "",
+ "features": "Margaritas",
+ "date": "02/17/18"
+ },
+ {
+ "id": "10",
+ "name": "Chipotle Minneapolis",
+ "lat": "44.972808",
+ "lng": "-93.247153",
+ "category": "Restaurant, Coffee",
+ "address": "229 Cedar Ave S",
+ "address2": "",
+ "city": "Minneapolis",
+ "state": "MN",
+ "postal": "55454",
+ "phone": "612-659-7830",
+ "web": "http:\/\/www.chipotle.com",
+ "hours1": "Mon-Sun 11am-10pm",
+ "hours2": "",
+ "hours3": "",
+ "featured": "",
+ "features": "",
+ "date": "01/17/18"
+ },
+ {
+ "id": "11",
+ "name": "Chipotle Minneapolis",
+ "lat": "44.987687",
+ "lng": "-93.257581",
+ "category": "Restaurant",
+ "address": "225 Hennepin Ave E",
+ "address2": "",
+ "city": "Minneapolis",
+ "state": "MN",
+ "postal": "55414",
+ "phone": "612-331-6330",
+ "web": "http:\/\/www.chipotle.com",
+ "hours1": "Mon-Sun 11am-10pm",
+ "hours2": "",
+ "hours3": "",
+ "featured": "",
+ "features": "Fresh Guacamole",
+ "date": "12/17/17"
+ },
+ {
+ "id": "12",
+ "name": "Chipotle Minneapolis",
+ "lat": "44.973665",
+ "lng": "-93.227023",
+ "category": "Restaurant",
+ "address": "800 Washington Ave SE",
+ "address2": "",
+ "city": "Minneapolis",
+ "state": "MN",
+ "postal": "55414",
+ "phone": "612-378-7078",
+ "web": "http:\/\/www.chipotle.com",
+ "hours1": "Mon-Sun 11am-10pm",
+ "hours2": "",
+ "hours3": "",
+ "featured": "",
+ "features": "",
+ "date": "11/17/17"
+ },
+ {
+ "id": "13",
+ "name": "Chipotle Bloomington",
+ "lat": "44.8458631",
+ "lng": "-93.2860161",
+ "category": "Restaurant",
+ "address": "322 South Ave",
+ "address2": "",
+ "city": "Bloomington",
+ "state": "MN",
+ "postal": "55425",
+ "phone": "952-252-3800",
+ "web": "http:\/\/www.chipotle.com",
+ "hours1": "Mon-Sun 11am-10pm",
+ "hours2": "",
+ "hours3": "",
+ "featured": "",
+ "features": "Online Ordering",
+ "date": "10/17/17"
+ },
+ {
+ "id": "14",
+ "name": "Chipotle Wayzata",
+ "lat": "44.9716626",
+ "lng": "-93.4967757",
+ "category": "Restaurant",
+ "address": "1313 Wayzata Blvd",
+ "address2": "",
+ "city": "Wayzata",
+ "state": "MN",
+ "postal": "55391",
+ "phone": "952-473-7100",
+ "web": "http:\/\/www.chipotle.com",
+ "hours1": "Mon-Sun 11am-10pm",
+ "hours2": "",
+ "hours3": "",
+ "featured": "",
+ "features": "Margaritas",
+ "date": "09/17/17"
+ },
+ {
+ "id": "15",
+ "name": "Chipotle Eden Prairie",
+ "lat": "44.859761",
+ "lng": "-93.436379",
+ "category": "Restaurant, Bar",
+ "address": "13250 Technology Dr",
+ "address2": "",
+ "city": "Eden Prairie",
+ "state": "MN",
+ "postal": "55344",
+ "phone": "952-934-5955",
+ "web": "http:\/\/www.chipotle.com",
+ "hours1": "Mon-Sun 11am-10pm",
+ "hours2": "",
+ "hours3": "",
+ "featured": "true",
+ "features": "Online Ordering",
+ "date": "08/17/17"
+ },
+ {
+ "id": "16",
+ "name": "Chipotle Plymouth",
+ "lat": "45.019846",
+ "lng": "-93.481832",
+ "category": "Restaurant",
+ "address": "3425 Vicksburg Lane N",
+ "address2": "",
+ "city": "Plymouth",
+ "state": "MN",
+ "postal": "55447",
+ "phone": "763-519-0063",
+ "web": "http:\/\/www.chipotle.com",
+ "hours1": "Mon-Sun 11am-10pm",
+ "hours2": "",
+ "hours3": "",
+ "featured": "",
+ "features": "Online Ordering, Fresh Guacamole",
+ "date": "07/17/17"
+ },
+ {
+ "id": "17",
+ "name": "Chipotle Roseville",
+ "lat": "44.998965",
+ "lng": "-93.194622",
+ "category": "Restaurant, Coffee",
+ "address": "860 Rosedale Center Plaza",
+ "address2": "",
+ "city": "Roseville",
+ "state": "MN",
+ "postal": "55113",
+ "phone": "651-633-2300",
+ "web": "http:\/\/www.chipotle.com",
+ "hours1": "Mon-Sun 11am-10pm",
+ "hours2": "",
+ "hours3": "",
+ "featured": "",
+ "features": "",
+ "date": "06/17/17"
+ },
+ {
+ "id": "18",
+ "name": "Chipotle St. Paul",
+ "lat": "44.939865",
+ "lng": "-93.136768",
+ "category": "Restaurant, Bakery",
+ "address": "867 Grand Ave",
+ "address2": "",
+ "city": "St. Paul",
+ "state": "MN",
+ "postal": "55105",
+ "phone": "651-602-0560",
+ "web": "http:\/\/www.chipotle.com",
+ "hours1": "Mon-Sun 11am-10pm",
+ "hours2": "",
+ "hours3": "",
+ "featured": "",
+ "features": "",
+ "date": "05/17/17"
+ },
+ {
+ "id": "19",
+ "name": "Chipotle Chanhassen",
+ "lat": "44.858736",
+ "lng": "-93.533661",
+ "category": "Restaurant, Bar",
+ "address": "560 W 79th",
+ "address2": "",
+ "city": "Chanhassen",
+ "state": "MN",
+ "postal": "55317",
+ "phone": "952-294-0301",
+ "web": "http:\/\/www.chipotle.com",
+ "hours1": "Mon-Sun 11am-10pm",
+ "hours2": "",
+ "hours3": "",
+ "featured": "",
+ "features": "Online Ordering, Fresh Guacamole",
+ "date": "04/17/17"
+ },
+ {
+ "id": "20",
+ "name": "Chipotle St. Paul",
+ "lat": "44.945127",
+ "lng": "-93.095368",
+ "category": "Restaurant, Coffee",
+ "address": "29 5th St West",
+ "address2": "",
+ "city": "St. Paul",
+ "state": "MN",
+ "postal": "55102",
+ "phone": "651-291-5411",
+ "web": "http:\/\/www.chipotle.com",
+ "hours1": "Mon-Sun 11am-10pm",
+ "hours2": "",
+ "hours3": "",
+ "featured": "",
+ "features": "Fresh Guacamole",
+ "date": "03/17/17"
+ },
+ {
+ "id": "21",
+ "name": "Chipotle Hudson",
+ "lat": "44.9594046",
+ "lng": "-92.7208174",
+ "category": "Restaurant",
+ "address": "1021 Pearson Dr",
+ "address2": "",
+ "city": "Hudson",
+ "state": "WI",
+ "postal": "54016",
+ "phone": "715-386-3010",
+ "web": "http:\/\/www.chipotle.com",
+ "hours1": "Mon-Sun 11am-10pm",
+ "hours2": "",
+ "hours3": "",
+ "featured": "",
+ "features": "Online Ordering",
+ "date": "03/17/23"
+ }
+]
diff --git a/dist/data/locations.xml b/dist/data/locations.xml
index 4edbe8b..36eb49a 100755
--- a/dist/data/locations.xml
+++ b/dist/data/locations.xml
@@ -1,7 +1,7 @@
-
+
@@ -20,4 +20,4 @@
-
\ No newline at end of file
+
diff --git a/dist/default-location-example.html b/dist/default-location-example.html
index a06657d..318375d 100755
--- a/dist/default-location-example.html
+++ b/dist/default-location-example.html
@@ -1,5 +1,5 @@
-
+
Map Example - Default Location
@@ -7,47 +7,49 @@
-
+
-
+
-
-
+
-
+
-
\ No newline at end of file
+
diff --git a/dist/fullmapstartblank-example.html b/dist/fullmapstartblank-example.html
new file mode 100644
index 0000000..7394d0e
--- /dev/null
+++ b/dist/fullmapstartblank-example.html
@@ -0,0 +1,56 @@
+
+
+
+
Map Example - fullMapStartBlank
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dist/geocode.html b/dist/geocode.html
index 2fc32bc..7068a87 100755
--- a/dist/geocode.html
+++ b/dist/geocode.html
@@ -1,5 +1,5 @@
-
+
Geocode
@@ -16,7 +16,7 @@
-
+
-
+
Submit
@@ -36,8 +36,8 @@
Simple geocoding form
-
-
+
+
diff --git a/dist/index.html b/dist/index.html
index e14129b..01976b3 100755
--- a/dist/index.html
+++ b/dist/index.html
@@ -1,5 +1,5 @@
-
+
Map Example
@@ -7,42 +7,45 @@
-
+
-
+
-
-
+
-
+
-
\ No newline at end of file
+
diff --git a/dist/infobubble-example.html b/dist/infobubble-example.html
new file mode 100644
index 0000000..319e823
--- /dev/null
+++ b/dist/infobubble-example.html
@@ -0,0 +1,63 @@
+
+
+
+
Infobubble Example
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dist/inline-directions.html b/dist/inline-directions.html
index f404885..0b94320 100644
--- a/dist/inline-directions.html
+++ b/dist/inline-directions.html
@@ -1,5 +1,5 @@
-
+
Map Example - Inline Directions
@@ -10,7 +10,7 @@
@@ -22,29 +22,31 @@
Using Chipotle as an Example
-
Submit
+
Search
-
-
+
-
+
-
\ No newline at end of file
+
diff --git a/dist/json-example.html b/dist/json-example.html
index 53a04a6..3d70edb 100755
--- a/dist/json-example.html
+++ b/dist/json-example.html
@@ -1,5 +1,5 @@
-
+
Map Example - JSON Data
@@ -7,45 +7,47 @@
-
+
-
+
-
-
+
-
+
-
+
-
\ No newline at end of file
+
diff --git a/dist/kml-example.html b/dist/kml-example.html
index d3dc43f..c6bb566 100755
--- a/dist/kml-example.html
+++ b/dist/kml-example.html
@@ -1,5 +1,5 @@
-
+
Map Example - KML Data
@@ -7,45 +7,47 @@
-
+
-
+
-
-
+
-
+
-
+
-
\ No newline at end of file
+
diff --git a/dist/lazy-load-example.html b/dist/lazy-load-example.html
new file mode 100644
index 0000000..df72247
--- /dev/null
+++ b/dist/lazy-load-example.html
@@ -0,0 +1,64 @@
+
+
+
+
Map Example
+
+
+
+
+
+
+
+
+
+
Agree to cookies
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dist/length-unit-swap-example.html b/dist/length-unit-swap-example.html
new file mode 100644
index 0000000..a21613e
--- /dev/null
+++ b/dist/length-unit-swap-example.html
@@ -0,0 +1,57 @@
+
+
+
+
Length Unit Swap Example
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dist/maxdistance-example.html b/dist/maxdistance-example.html
index c670fbb..59130bf 100755
--- a/dist/maxdistance-example.html
+++ b/dist/maxdistance-example.html
@@ -1,5 +1,5 @@
-
+
Map Example - Maximum Distance
@@ -7,14 +7,14 @@
-
+
-
+
-
-
+
-
+
diff --git a/dist/modal-example.html b/dist/modal-example.html
index 9cfd24e..c5d17a9 100755
--- a/dist/modal-example.html
+++ b/dist/modal-example.html
@@ -1,5 +1,5 @@
-
+
Map Example - Modal Window
@@ -7,45 +7,47 @@
-
+
-
+
-
-
+
-
+
-
+
diff --git a/dist/namesearch-example.html b/dist/namesearch-example.html
index f67d063..d427962 100644
--- a/dist/namesearch-example.html
+++ b/dist/namesearch-example.html
@@ -1,5 +1,5 @@
-
+
NameSearch Example
@@ -10,7 +10,7 @@
-
-
+
-
+
-
\ No newline at end of file
+
diff --git a/dist/noform-example.html b/dist/noform-example.html
index b25c8ee..dd2bedc 100755
--- a/dist/noform-example.html
+++ b/dist/noform-example.html
@@ -1,5 +1,5 @@
-
+
Map Example - No Form for ASP.net
@@ -7,39 +7,43 @@
-
+
-
+
-
-
+
-
+
-
+
-
\ No newline at end of file
+
diff --git a/dist/pagination-example.html b/dist/pagination-example.html
index 0cd8d41..c82b04d 100644
--- a/dist/pagination-example.html
+++ b/dist/pagination-example.html
@@ -1,5 +1,5 @@
-
+
Map Example - Pagination
@@ -10,45 +10,46 @@
-
+
-
-
-
-
+
+
-
+
-
+
-
\ No newline at end of file
+
diff --git a/dist/query-string-example/index.html b/dist/query-string-example/index.html
new file mode 100644
index 0000000..9eaf051
--- /dev/null
+++ b/dist/query-string-example/index.html
@@ -0,0 +1,31 @@
+
+
+
+
Map Example
+
+
+
+
+
+
+
+
+
+
diff --git a/dist/query-string-example/submit.html b/dist/query-string-example/submit.html
new file mode 100644
index 0000000..bc983c6
--- /dev/null
+++ b/dist/query-string-example/submit.html
@@ -0,0 +1,61 @@
+
+
+
+
Map Example
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dist/rawdata-example.php b/dist/rawdata-example.php
new file mode 100644
index 0000000..d1b38f3
--- /dev/null
+++ b/dist/rawdata-example.php
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ';
+
+?>
+
+
+
+
+
Map Example - Raw Data
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dist/sort-example.html b/dist/sort-example.html
new file mode 100644
index 0000000..aa23600
--- /dev/null
+++ b/dist/sort-example.html
@@ -0,0 +1,77 @@
+
+
+
+
Sorting Example
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/libs/handlebars/handlebars-4.7.7.js b/libs/handlebars/handlebars-4.7.7.js
new file mode 100644
index 0000000..fb23399
--- /dev/null
+++ b/libs/handlebars/handlebars-4.7.7.js
@@ -0,0 +1,29 @@
+/**!
+
+ @license
+ handlebars v4.7.7
+
+ Copyright (C) 2011-2019 by Yehuda Katz
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+
+ */
+!function(a,b){"object"==typeof exports&&"object"==typeof module?module.exports=b():"function"==typeof define&&define.amd?define([],b):"object"==typeof exports?exports.Handlebars=b():a.Handlebars=b()}(this,function(){return function(a){function b(d){if(c[d])return c[d].exports;var e=c[d]={exports:{},id:d,loaded:!1};return a[d].call(e.exports,e,e.exports,b),e.loaded=!0,e.exports}var c={};return b.m=a,b.c=c,b.p="",b(0)}([function(a,b,c){"use strict";function d(){var a=r();return a.compile=function(b,c){return k.compile(b,c,a)},a.precompile=function(b,c){return k.precompile(b,c,a)},a.AST=i["default"],a.Compiler=k.Compiler,a.JavaScriptCompiler=m["default"],a.Parser=j.parser,a.parse=j.parse,a.parseWithoutProcessing=j.parseWithoutProcessing,a}var e=c(1)["default"];b.__esModule=!0;var f=c(2),g=e(f),h=c(45),i=e(h),j=c(46),k=c(51),l=c(52),m=e(l),n=c(49),o=e(n),p=c(44),q=e(p),r=g["default"].create,s=d();s.create=d,q["default"](s),s.Visitor=o["default"],s["default"]=s,b["default"]=s,a.exports=b["default"]},function(a,b){"use strict";b["default"]=function(a){return a&&a.__esModule?a:{"default":a}},b.__esModule=!0},function(a,b,c){"use strict";function d(){var a=new h.HandlebarsEnvironment;return n.extend(a,h),a.SafeString=j["default"],a.Exception=l["default"],a.Utils=n,a.escapeExpression=n.escapeExpression,a.VM=p,a.template=function(b){return p.template(b,a)},a}var e=c(3)["default"],f=c(1)["default"];b.__esModule=!0;var g=c(4),h=e(g),i=c(37),j=f(i),k=c(6),l=f(k),m=c(5),n=e(m),o=c(38),p=e(o),q=c(44),r=f(q),s=d();s.create=d,r["default"](s),s["default"]=s,b["default"]=s,a.exports=b["default"]},function(a,b){"use strict";b["default"]=function(a){if(a&&a.__esModule)return a;var b={};if(null!=a)for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&(b[c]=a[c]);return b["default"]=a,b},b.__esModule=!0},function(a,b,c){"use strict";function d(a,b,c){this.helpers=a||{},this.partials=b||{},this.decorators=c||{},i.registerDefaultHelpers(this),j.registerDefaultDecorators(this)}var e=c(1)["default"];b.__esModule=!0,b.HandlebarsEnvironment=d;var f=c(5),g=c(6),h=e(g),i=c(10),j=c(30),k=c(32),l=e(k),m=c(33),n="4.7.7";b.VERSION=n;var o=8;b.COMPILER_REVISION=o;var p=7;b.LAST_COMPATIBLE_COMPILER_REVISION=p;var q={1:"<= 1.0.rc.2",2:"== 1.0.0-rc.3",3:"== 1.0.0-rc.4",4:"== 1.x.x",5:"== 2.0.0-alpha.x",6:">= 2.0.0-beta.1",7:">= 4.0.0 <4.3.0",8:">= 4.3.0"};b.REVISION_CHANGES=q;var r="[object Object]";d.prototype={constructor:d,logger:l["default"],log:l["default"].log,registerHelper:function(a,b){if(f.toString.call(a)===r){if(b)throw new h["default"]("Arg not supported with multiple helpers");f.extend(this.helpers,a)}else this.helpers[a]=b},unregisterHelper:function(a){delete this.helpers[a]},registerPartial:function(a,b){if(f.toString.call(a)===r)f.extend(this.partials,a);else{if("undefined"==typeof b)throw new h["default"]('Attempting to register a partial called "'+a+'" as undefined');this.partials[a]=b}},unregisterPartial:function(a){delete this.partials[a]},registerDecorator:function(a,b){if(f.toString.call(a)===r){if(b)throw new h["default"]("Arg not supported with multiple decorators");f.extend(this.decorators,a)}else this.decorators[a]=b},unregisterDecorator:function(a){delete this.decorators[a]},resetLoggedPropertyAccesses:function(){m.resetLoggedProperties()}};var s=l["default"].log;b.log=s,b.createFrame=f.createFrame,b.logger=l["default"]},function(a,b){"use strict";function c(a){return k[a]}function d(a){for(var b=1;b
":">",'"':""","'":"'","`":"`","=":"="},l=/[&<>"'`=]/g,m=/[&<>"'`=]/,n=Object.prototype.toString;b.toString=n;var o=function(a){return"function"==typeof a};o(/x/)&&(b.isFunction=o=function(a){return"function"==typeof a&&"[object Function]"===n.call(a)}),b.isFunction=o;var p=Array.isArray||function(a){return!(!a||"object"!=typeof a)&&"[object Array]"===n.call(a)};b.isArray=p},function(a,b,c){"use strict";function d(a,b){var c=b&&b.loc,g=void 0,h=void 0,i=void 0,j=void 0;c&&(g=c.start.line,h=c.end.line,i=c.start.column,j=c.end.column,a+=" - "+g+":"+i);for(var k=Error.prototype.constructor.call(this,a),l=0;l0?(c.ids&&(c.ids=[c.name]),a.helpers.each(b,c)):e(this);if(c.data&&c.ids){var g=d.createFrame(c.data);g.contextPath=d.appendContextPath(c.data.contextPath,c.name),c={data:g}}return f(b,c)})},a.exports=b["default"]},function(a,b,c){(function(d){"use strict";var e=c(13)["default"],f=c(1)["default"];b.__esModule=!0;var g=c(5),h=c(6),i=f(h);b["default"]=function(a){a.registerHelper("each",function(a,b){function c(b,c,d){l&&(l.key=b,l.index=c,l.first=0===c,l.last=!!d,m&&(l.contextPath=m+b)),k+=f(a[b],{data:l,blockParams:g.blockParams([a[b],b],[m+b,null])})}if(!b)throw new i["default"]("Must pass iterator to #each");var f=b.fn,h=b.inverse,j=0,k="",l=void 0,m=void 0;if(b.data&&b.ids&&(m=g.appendContextPath(b.data.contextPath,b.ids[0])+"."),g.isFunction(a)&&(a=a.call(this)),b.data&&(l=g.createFrame(b.data)),a&&"object"==typeof a)if(g.isArray(a))for(var n=a.length;j=0?b:parseInt(a,10)}return a},log:function(a){if(a=e.lookupLevel(a),"undefined"!=typeof console&&e.lookupLevel(e.level)<=a){var b=e.methodMap[a];console[b]||(b="log");for(var c=arguments.length,d=Array(c>1?c-1:0),f=1;f=v.LAST_COMPATIBLE_COMPILER_REVISION&&b<=v.COMPILER_REVISION)){if(b2&&v.push("'"+this.terminals_[s]+"'");x=this.lexer.showPosition?"Parse error on line "+(i+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+v.join(", ")+", got '"+(this.terminals_[n]||n)+"'":"Parse error on line "+(i+1)+": Unexpected "+(1==n?"end of input":"'"+(this.terminals_[n]||n)+"'"),this.parseError(x,{text:this.lexer.match,token:this.terminals_[n]||n,line:this.lexer.yylineno,loc:l,expected:v})}}if(q[0]instanceof Array&&q.length>1)throw new Error("Parse Error: multiple actions possible at state: "+p+", token: "+n);switch(q[0]){case 1:d.push(n),e.push(this.lexer.yytext),f.push(this.lexer.yylloc),d.push(q[1]),n=null,o?(n=o,o=null):(j=this.lexer.yyleng,h=this.lexer.yytext,i=this.lexer.yylineno,l=this.lexer.yylloc,k>0&&k--);break;case 2:if(t=this.productions_[q[1]][1],w.$=e[e.length-t],w._$={first_line:f[f.length-(t||1)].first_line,last_line:f[f.length-1].last_line,first_column:f[f.length-(t||1)].first_column,last_column:f[f.length-1].last_column},m&&(w._$.range=[f[f.length-(t||1)].range[0],f[f.length-1].range[1]]),r=this.performAction.call(w,h,j,i,this.yy,q[1],e,f),"undefined"!=typeof r)return r;t&&(d=d.slice(0,-1*t*2),e=e.slice(0,-1*t),f=f.slice(0,-1*t)),d.push(this.productions_[q[1]][0]),e.push(w.$),f.push(w._$),u=g[d[d.length-2]][d[d.length-1]],d.push(u);break;case 3:return!0}}return!0}},c=function(){var a={EOF:1,parseError:function(a,b){if(!this.yy.parser)throw new Error(a);this.yy.parser.parseError(a,b)},setInput:function(a){return this._input=a,this._more=this._less=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var a=this._input[0];this.yytext+=a,this.yyleng++,this.offset++,this.match+=a,this.matched+=a;var b=a.match(/(?:\r\n?|\n).*/g);return b?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),a},unput:function(a){var b=a.length,c=a.split(/(?:\r\n?|\n)/g);this._input=a+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-b-1),this.offset-=b;var d=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),c.length-1&&(this.yylineno-=c.length-1);var e=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:c?(c.length===d.length?this.yylloc.first_column:0)+d[d.length-c.length].length-c[0].length:this.yylloc.first_column-b},this.options.ranges&&(this.yylloc.range=[e[0],e[0]+this.yyleng-b]),this},more:function(){return this._more=!0,this},less:function(a){this.unput(this.match.slice(a))},pastInput:function(){var a=this.matched.substr(0,this.matched.length-this.match.length);return(a.length>20?"...":"")+a.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var a=this.match;return a.length<20&&(a+=this._input.substr(0,20-a.length)),(a.substr(0,20)+(a.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var a=this.pastInput(),b=new Array(a.length+1).join("-");return a+this.upcomingInput()+"\n"+b+"^"},next:function(){if(this.done)return this.EOF;this._input||(this.done=!0);var a,b,c,d,e;this._more||(this.yytext="",this.match="");for(var f=this._currentRules(),g=0;gb[0].length)||(b=c,d=g,this.options.flex));g++);return b?(e=b[0].match(/(?:\r\n?|\n).*/g),e&&(this.yylineno+=e.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:e?e[e.length-1].length-e[e.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+b[0].length},this.yytext+=b[0],this.match+=b[0],this.matches=b,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._input=this._input.slice(b[0].length),this.matched+=b[0],a=this.performAction.call(this,this.yy,this,f[d],this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),a?a:void 0):""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var a=this.next();return"undefined"!=typeof a?a:this.lex()},begin:function(a){this.conditionStack.push(a)},popState:function(){return this.conditionStack.pop()},_currentRules:function(){return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules},topState:function(){return this.conditionStack[this.conditionStack.length-2]},pushState:function(a){this.begin(a)}};return a.options={},a.performAction=function(a,b,c,d){function e(a,c){return b.yytext=b.yytext.substring(a,b.yyleng-c+a)}switch(c){case 0:if("\\\\"===b.yytext.slice(-2)?(e(0,1),this.begin("mu")):"\\"===b.yytext.slice(-1)?(e(0,1),this.begin("emu")):this.begin("mu"),b.yytext)return 15;break;case 1:return 15;case 2:return this.popState(),15;case 3:return this.begin("raw"),15;case 4:return this.popState(),"raw"===this.conditionStack[this.conditionStack.length-1]?15:(e(5,9),"END_RAW_BLOCK");case 5:return 15;case 6:return this.popState(),14;case 7:return 65;case 8:return 68;case 9:return 19;case 10:return this.popState(),this.begin("raw"),23;case 11:return 55;case 12:return 60;case 13:return 29;case 14:return 47;case 15:return this.popState(),44;case 16:return this.popState(),44;case 17:return 34;case 18:return 39;case 19:return 51;case 20:return 48;case 21:this.unput(b.yytext),this.popState(),this.begin("com");break;case 22:return this.popState(),14;case 23:return 48;case 24:return 73;case 25:return 72;case 26:return 72;case 27:return 87;case 28:break;case 29:return this.popState(),54;case 30:return this.popState(),33;case 31:return b.yytext=e(1,2).replace(/\\"/g,'"'),80;case 32:return b.yytext=e(1,2).replace(/\\'/g,"'"),80;case 33:return 85;case 34:return 82;case 35:return 82;case 36:return 83;case 37:return 84;case 38:return 81;case 39:return 75;case 40:return 77;case 41:return 72;case 42:return b.yytext=b.yytext.replace(/\\([\\\]])/g,"$1"),72;case 43:return"INVALID";case 44:return 5}},a.rules=[/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/,/^(?:\{\{\{\{(?=[^\/]))/,/^(?:\{\{\{\{\/[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.])\}\}\}\})/,/^(?:[^\x00]+?(?=(\{\{\{\{)))/,/^(?:[\s\S]*?--(~)?\}\})/,/^(?:\()/,/^(?:\))/,/^(?:\{\{\{\{)/,/^(?:\}\}\}\})/,/^(?:\{\{(~)?>)/,/^(?:\{\{(~)?#>)/,/^(?:\{\{(~)?#\*?)/,/^(?:\{\{(~)?\/)/,/^(?:\{\{(~)?\^\s*(~)?\}\})/,/^(?:\{\{(~)?\s*else\s*(~)?\}\})/,/^(?:\{\{(~)?\^)/,/^(?:\{\{(~)?\s*else\b)/,/^(?:\{\{(~)?\{)/,/^(?:\{\{(~)?&)/,/^(?:\{\{(~)?!--)/,/^(?:\{\{(~)?![\s\S]*?\}\})/,/^(?:\{\{(~)?\*?)/,/^(?:=)/,/^(?:\.\.)/,/^(?:\.(?=([=~}\s\/.)|])))/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}(~)?\}\})/,/^(?:(~)?\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=([~}\s)])))/,/^(?:false(?=([~}\s)])))/,/^(?:undefined(?=([~}\s)])))/,/^(?:null(?=([~}\s)])))/,/^(?:-?[0-9]+(?:\.[0-9]+)?(?=([~}\s)])))/,/^(?:as\s+\|)/,/^(?:\|)/,/^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)|]))))/,/^(?:\[(\\\]|[^\]])*\])/,/^(?:.)/,/^(?:$)/],a.conditions={mu:{rules:[7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44],inclusive:!1},emu:{rules:[2],inclusive:!1},com:{rules:[6],inclusive:!1},raw:{rules:[3,4,5],inclusive:!1},INITIAL:{rules:[0,1,44],inclusive:!0}},a}();return b.lexer=c,a.prototype=b,b.Parser=a,new a}();b["default"]=c,a.exports=b["default"]},function(a,b,c){"use strict";function d(){var a=arguments.length<=0||void 0===arguments[0]?{}:arguments[0];this.options=a}function e(a,b,c){void 0===b&&(b=a.length);var d=a[b-1],e=a[b-2];return d?"ContentStatement"===d.type?(e||!c?/\r?\n\s*?$/:/(^|\r?\n)\s*?$/).test(d.original):void 0:c}function f(a,b,c){void 0===b&&(b=-1);var d=a[b+1],e=a[b+2];return d?"ContentStatement"===d.type?(e||!c?/^\s*?\r?\n/:/^\s*?(\r?\n|$)/).test(d.original):void 0:c}function g(a,b,c){var d=a[null==b?0:b+1];if(d&&"ContentStatement"===d.type&&(c||!d.rightStripped)){var e=d.value;d.value=d.value.replace(c?/^\s+/:/^[ \t]*\r?\n?/,""),d.rightStripped=d.value!==e}}function h(a,b,c){var d=a[null==b?a.length-1:b-1];if(d&&"ContentStatement"===d.type&&(c||!d.leftStripped)){var e=d.value;return d.value=d.value.replace(c?/\s+$/:/[ \t]+$/,""),d.leftStripped=d.value!==e,d.leftStripped}}var i=c(1)["default"];b.__esModule=!0;var j=c(49),k=i(j);d.prototype=new k["default"],d.prototype.Program=function(a){var b=!this.options.ignoreStandalone,c=!this.isRootSeen;this.isRootSeen=!0;for(var d=a.body,i=0,j=d.length;i0)throw new q["default"]("Invalid path: "+d,{loc:c});".."===i&&f++}}return{type:"PathExpression",data:a,depth:f,parts:e,original:d,loc:c}}function j(a,b,c,d,e,f){var g=d.charAt(3)||d.charAt(2),h="{"!==g&&"&"!==g,i=/\*/.test(d);return{type:i?"Decorator":"MustacheStatement",path:a,params:b,hash:c,escaped:h,strip:e,loc:this.locInfo(f)}}function k(a,b,c,e){d(a,c),e=this.locInfo(e);var f={type:"Program",body:b,strip:{},loc:e};return{type:"BlockStatement",path:a.path,params:a.params,hash:a.hash,program:f,openStrip:{},inverseStrip:{},closeStrip:{},loc:e}}function l(a,b,c,e,f,g){e&&e.path&&d(a,e);var h=/\*/.test(a.open);b.blockParams=a.blockParams;var i=void 0,j=void 0;if(c){if(h)throw new q["default"]("Unexpected inverse block on decorator",c);c.chain&&(c.program.body[0].closeStrip=e.strip),j=c.strip,i=c.program}return f&&(f=i,i=b,b=f),{type:h?"DecoratorBlock":"BlockStatement",path:a.path,params:a.params,hash:a.hash,program:b,inverse:i,openStrip:a.strip,inverseStrip:j,closeStrip:e&&e.strip,loc:this.locInfo(g)}}function m(a,b){if(!b&&a.length){var c=a[0].loc,d=a[a.length-1].loc;c&&d&&(b={source:c.source,start:{line:c.start.line,column:c.start.column},end:{line:d.end.line,column:d.end.column}})}return{type:"Program",body:a,strip:{},loc:b}}function n(a,b,c,e){return d(a,c),{type:"PartialBlockStatement",name:a.path,params:a.params,hash:a.hash,program:b,openStrip:a.strip,closeStrip:c&&c.strip,loc:this.locInfo(e)}}var o=c(1)["default"];b.__esModule=!0,b.SourceLocation=e,b.id=f,b.stripFlags=g,b.stripComment=h,b.preparePath=i,b.prepareMustache=j,b.prepareRawBlock=k,b.prepareBlock=l,b.prepareProgram=m,b.preparePartialBlock=n;var p=c(6),q=o(p)},function(a,b,c){"use strict";function d(){}function e(a,b,c){if(null==a||"string"!=typeof a&&"Program"!==a.type)throw new l["default"]("You must pass a string or Handlebars AST to Handlebars.precompile. You passed "+a);b=b||{},"data"in b||(b.data=!0),b.compat&&(b.useDepths=!0);var d=c.parse(a,b),e=(new c.Compiler).compile(d,b);return(new c.JavaScriptCompiler).compile(e,b)}function f(a,b,c){function d(){var d=c.parse(a,b),e=(new c.Compiler).compile(d,b),f=(new c.JavaScriptCompiler).compile(e,b,void 0,!0);return c.template(f)}function e(a,b){return f||(f=d()),f.call(this,a,b)}if(void 0===b&&(b={}),null==a||"string"!=typeof a&&"Program"!==a.type)throw new l["default"]("You must pass a string or Handlebars AST to Handlebars.compile. You passed "+a);b=m.extend({},b),"data"in b||(b.data=!0),b.compat&&(b.useDepths=!0);var f=void 0;return e._setup=function(a){return f||(f=d()),f._setup(a)},e._child=function(a,b,c,e){return f||(f=d()),f._child(a,b,c,e)},e}function g(a,b){if(a===b)return!0;if(m.isArray(a)&&m.isArray(b)&&a.length===b.length){for(var c=0;c1)throw new l["default"]("Unsupported number of partial arguments: "+c.length,a);c.length||(this.options.explicitPartialContext?this.opcode("pushLiteral","undefined"):c.push({type:"PathExpression",parts:[],depth:0}));var d=a.name.original,e="SubExpression"===a.name.type;e&&this.accept(a.name),this.setupFullMustacheParams(a,b,void 0,!0);var f=a.indent||"";this.options.preventIndent&&f&&(this.opcode("appendContent",f),f=""),this.opcode("invokePartial",e,d,f),this.opcode("append")},PartialBlockStatement:function(a){this.PartialStatement(a)},MustacheStatement:function(a){this.SubExpression(a),a.escaped&&!this.options.noEscape?this.opcode("appendEscaped"):this.opcode("append")},Decorator:function(a){this.DecoratorBlock(a)},ContentStatement:function(a){a.value&&this.opcode("appendContent",a.value)},CommentStatement:function(){},SubExpression:function(a){h(a);var b=this.classifySexpr(a);"simple"===b?this.simpleSexpr(a):"helper"===b?this.helperSexpr(a):this.ambiguousSexpr(a)},ambiguousSexpr:function(a,b,c){var d=a.path,e=d.parts[0],f=null!=b||null!=c;this.opcode("getContext",d.depth),this.opcode("pushProgram",b),this.opcode("pushProgram",c),d.strict=!0,this.accept(d),this.opcode("invokeAmbiguous",e,f)},simpleSexpr:function(a){var b=a.path;b.strict=!0,this.accept(b),this.opcode("resolvePossibleLambda")},helperSexpr:function(a,b,c){var d=this.setupFullMustacheParams(a,b,c),e=a.path,f=e.parts[0];if(this.options.knownHelpers[f])this.opcode("invokeKnownHelper",d.length,f);else{if(this.options.knownHelpersOnly)throw new l["default"]("You specified knownHelpersOnly, but used the unknown helper "+f,a);e.strict=!0,e.falsy=!0,this.accept(e),this.opcode("invokeHelper",d.length,e.original,o["default"].helpers.simpleId(e))}},PathExpression:function(a){this.addDepth(a.depth),this.opcode("getContext",a.depth);var b=a.parts[0],c=o["default"].helpers.scopedId(a),d=!a.depth&&!c&&this.blockParamIndex(b);d?this.opcode("lookupBlockParam",d,a.parts):b?a.data?(this.options.data=!0,this.opcode("lookupData",a.depth,a.parts,a.strict)):this.opcode("lookupOnContext",a.parts,a.falsy,a.strict,c):this.opcode("pushContext")},StringLiteral:function(a){this.opcode("pushString",a.value)},NumberLiteral:function(a){this.opcode("pushLiteral",a.value)},BooleanLiteral:function(a){this.opcode("pushLiteral",a.value)},UndefinedLiteral:function(){this.opcode("pushLiteral","undefined")},NullLiteral:function(){this.opcode("pushLiteral","null")},Hash:function(a){var b=a.pairs,c=0,d=b.length;for(this.opcode("pushHash");c=0)return[b,e]}}}},function(a,b,c){"use strict";function d(a){this.value=a}function e(){}function f(a,b,c,d){var e=b.popStack(),f=0,g=c.length;for(a&&g--;f0&&(c+=", "+d.join(", "));var e=0;g(this.aliases).forEach(function(a){var d=b.aliases[a];d.children&&d.referenceCount>1&&(c+=", alias"+ ++e+"="+a,d.children[0]="alias"+e)}),this.lookupPropertyFunctionIsUsed&&(c+=", "+this.lookupPropertyFunctionVarDeclaration());var f=["container","depth0","helpers","partials","data"];(this.useBlockParams||this.useDepths)&&f.push("blockParams"),this.useDepths&&f.push("depths");var h=this.mergeSource(c);return a?(f.push(h),Function.apply(this,f)):this.source.wrap(["function(",f.join(","),") {\n ",h,"}"])},mergeSource:function(a){var b=this.environment.isSimple,c=!this.forceBuffer,d=void 0,e=void 0,f=void 0,g=void 0;return this.source.each(function(a){a.appendToBuffer?(f?a.prepend(" + "):f=a,g=a):(f&&(e?f.prepend("buffer += "):d=!0,g.add(";"),f=g=void 0),e=!0,b||(c=!1))}),c?f?(f.prepend("return "),g.add(";")):e||this.source.push('return "";'):(a+=", buffer = "+(d?"":this.initializeBuffer()),f?(f.prepend("return buffer + "),g.add(";")):this.source.push("return buffer;")),a&&this.source.prepend("var "+a.substring(2)+(d?"":";\n")),this.source.merge()},lookupPropertyFunctionVarDeclaration:function(){return"\n lookupProperty = container.lookupProperty || function(parent, propertyName) {\n if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {\n return parent[propertyName];\n }\n return undefined\n }\n ".trim()},blockValue:function(a){var b=this.aliasable("container.hooks.blockHelperMissing"),c=[this.contextName(0)];this.setupHelperArgs(a,0,c);var d=this.popStack();c.splice(1,0,d),this.push(this.source.functionCall(b,"call",c))},ambiguousBlockValue:function(){var a=this.aliasable("container.hooks.blockHelperMissing"),b=[this.contextName(0)];this.setupHelperArgs("",0,b,!0),this.flushInline();var c=this.topStack();b.splice(1,0,c),this.pushSource(["if (!",this.lastHelper,") { ",c," = ",this.source.functionCall(a,"call",b),"}"])},appendContent:function(a){this.pendingContent?a=this.pendingContent+a:this.pendingLocation=this.source.currentLocation,this.pendingContent=a},append:function(){if(this.isInline())this.replaceStack(function(a){return[" != null ? ",a,' : ""']}),this.pushSource(this.appendToBuffer(this.popStack()));else{var a=this.popStack();this.pushSource(["if (",a," != null) { ",this.appendToBuffer(a,void 0,!0)," }"]),this.environment.isSimple&&this.pushSource(["else { ",this.appendToBuffer("''",void 0,!0)," }"])}},appendEscaped:function(){this.pushSource(this.appendToBuffer([this.aliasable("container.escapeExpression"),"(",this.popStack(),")"]))},getContext:function(a){this.lastContext=a},pushContext:function(){this.pushStackLiteral(this.contextName(this.lastContext))},lookupOnContext:function(a,b,c,d){var e=0;d||!this.options.compat||this.lastContext?this.pushContext():this.push(this.depthedLookup(a[e++])),this.resolvePath("context",a,e,b,c)},lookupBlockParam:function(a,b){this.useBlockParams=!0,this.push(["blockParams[",a[0],"][",a[1],"]"]),this.resolvePath("context",b,1)},lookupData:function(a,b,c){a?this.pushStackLiteral("container.data(data, "+a+")"):this.pushStackLiteral("data"),this.resolvePath("data",b,0,!0,c)},resolvePath:function(a,b,c,d,e){var g=this;if(this.options.strict||this.options.assumeObjects)return void this.push(f(this.options.strict&&e,this,b,a));for(var h=b.length;cthis.stackVars.length&&this.stackVars.push("stack"+this.stackSlot),this.topStackName()},topStackName:function(){return"stack"+this.stackSlot},flushInline:function(){var a=this.inlineStack;this.inlineStack=[];for(var b=0,c=a.length;b <": "<",
- ">": ">",
- '"': """,
- "'": "'",
- "`": "`"
- };
-
- var badChars = /[&<>"'`]/g;
- var possible = /[&<>"'`]/;
-
- function escapeChar(chr) {
- return escape[chr];
- }
-
- function extend(obj /* , ...source */) {
- for (var i = 1; i < arguments.length; i++) {
- for (var key in arguments[i]) {
- if (Object.prototype.hasOwnProperty.call(arguments[i], key)) {
- obj[key] = arguments[i][key];
- }
- }
- }
-
- return obj;
- }
-
- __exports__.extend = extend;var toString = Object.prototype.toString;
- __exports__.toString = toString;
- // Sourced from lodash
- // https://github.com/bestiejs/lodash/blob/master/LICENSE.txt
- var isFunction = function(value) {
- return typeof value === 'function';
- };
- // fallback for older versions of Chrome and Safari
- /* istanbul ignore next */
- if (isFunction(/x/)) {
- isFunction = function(value) {
- return typeof value === 'function' && toString.call(value) === '[object Function]';
- };
- }
- var isFunction;
- __exports__.isFunction = isFunction;
- /* istanbul ignore next */
- var isArray = Array.isArray || function(value) {
- return (value && typeof value === 'object') ? toString.call(value) === '[object Array]' : false;
- };
- __exports__.isArray = isArray;
-
- function escapeExpression(string) {
- // don't escape SafeStrings, since they're already safe
- if (string instanceof SafeString) {
- return string.toString();
- } else if (string == null) {
- return "";
- } else if (!string) {
- return string + '';
- }
-
- // Force a string conversion as this will be done by the append regardless and
- // the regex test will do this transparently behind the scenes, causing issues if
- // an object's to string has escaped characters in it.
- string = "" + string;
-
- if(!possible.test(string)) { return string; }
- return string.replace(badChars, escapeChar);
- }
-
- __exports__.escapeExpression = escapeExpression;function isEmpty(value) {
- if (!value && value !== 0) {
- return true;
- } else if (isArray(value) && value.length === 0) {
- return true;
- } else {
- return false;
- }
- }
-
- __exports__.isEmpty = isEmpty;function appendContextPath(contextPath, id) {
- return (contextPath ? contextPath + '.' : '') + id;
- }
-
- __exports__.appendContextPath = appendContextPath;
- return __exports__;
-})(__module4__);
-
-// handlebars/exception.js
-var __module5__ = (function() {
- "use strict";
- var __exports__;
-
- var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
-
- function Exception(message, node) {
- var line;
- if (node && node.firstLine) {
- line = node.firstLine;
-
- message += ' - ' + line + ':' + node.firstColumn;
- }
-
- var tmp = Error.prototype.constructor.call(this, message);
-
- // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
- for (var idx = 0; idx < errorProps.length; idx++) {
- this[errorProps[idx]] = tmp[errorProps[idx]];
- }
-
- if (line) {
- this.lineNumber = line;
- this.column = node.firstColumn;
- }
- }
-
- Exception.prototype = new Error();
-
- __exports__ = Exception;
- return __exports__;
-})();
-
-// handlebars/base.js
-var __module2__ = (function(__dependency1__, __dependency2__) {
- "use strict";
- var __exports__ = {};
- var Utils = __dependency1__;
- var Exception = __dependency2__;
-
- var VERSION = "2.0.0";
- __exports__.VERSION = VERSION;var COMPILER_REVISION = 6;
- __exports__.COMPILER_REVISION = COMPILER_REVISION;
- var REVISION_CHANGES = {
- 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
- 2: '== 1.0.0-rc.3',
- 3: '== 1.0.0-rc.4',
- 4: '== 1.x.x',
- 5: '== 2.0.0-alpha.x',
- 6: '>= 2.0.0-beta.1'
- };
- __exports__.REVISION_CHANGES = REVISION_CHANGES;
- var isArray = Utils.isArray,
- isFunction = Utils.isFunction,
- toString = Utils.toString,
- objectType = '[object Object]';
-
- function HandlebarsEnvironment(helpers, partials) {
- this.helpers = helpers || {};
- this.partials = partials || {};
-
- registerDefaultHelpers(this);
- }
-
- __exports__.HandlebarsEnvironment = HandlebarsEnvironment;HandlebarsEnvironment.prototype = {
- constructor: HandlebarsEnvironment,
-
- logger: logger,
- log: log,
-
- registerHelper: function(name, fn) {
- if (toString.call(name) === objectType) {
- if (fn) { throw new Exception('Arg not supported with multiple helpers'); }
- Utils.extend(this.helpers, name);
- } else {
- this.helpers[name] = fn;
- }
- },
- unregisterHelper: function(name) {
- delete this.helpers[name];
- },
-
- registerPartial: function(name, partial) {
- if (toString.call(name) === objectType) {
- Utils.extend(this.partials, name);
- } else {
- this.partials[name] = partial;
- }
- },
- unregisterPartial: function(name) {
- delete this.partials[name];
- }
- };
-
- function registerDefaultHelpers(instance) {
- instance.registerHelper('helperMissing', function(/* [args, ]options */) {
- if(arguments.length === 1) {
- // A missing field in a {{foo}} constuct.
- return undefined;
- } else {
- // Someone is actually trying to call something, blow up.
- throw new Exception("Missing helper: '" + arguments[arguments.length-1].name + "'");
- }
- });
-
- instance.registerHelper('blockHelperMissing', function(context, options) {
- var inverse = options.inverse,
- fn = options.fn;
-
- if(context === true) {
- return fn(this);
- } else if(context === false || context == null) {
- return inverse(this);
- } else if (isArray(context)) {
- if(context.length > 0) {
- if (options.ids) {
- options.ids = [options.name];
- }
-
- return instance.helpers.each(context, options);
- } else {
- return inverse(this);
- }
- } else {
- if (options.data && options.ids) {
- var data = createFrame(options.data);
- data.contextPath = Utils.appendContextPath(options.data.contextPath, options.name);
- options = {data: data};
- }
-
- return fn(context, options);
- }
- });
-
- instance.registerHelper('each', function(context, options) {
- if (!options) {
- throw new Exception('Must pass iterator to #each');
- }
-
- var fn = options.fn, inverse = options.inverse;
- var i = 0, ret = "", data;
-
- var contextPath;
- if (options.data && options.ids) {
- contextPath = Utils.appendContextPath(options.data.contextPath, options.ids[0]) + '.';
- }
-
- if (isFunction(context)) { context = context.call(this); }
-
- if (options.data) {
- data = createFrame(options.data);
- }
-
- if(context && typeof context === 'object') {
- if (isArray(context)) {
- for(var j = context.length; i <= level) {
- var method = logger.methodMap[level];
- if (typeof console !== 'undefined' && console[method]) {
- console[method].call(console, message);
- }
- }
- }
- };
- __exports__.logger = logger;
- var log = logger.log;
- __exports__.log = log;
- var createFrame = function(object) {
- var frame = Utils.extend({}, object);
- frame._parent = object;
- return frame;
- };
- __exports__.createFrame = createFrame;
- return __exports__;
-})(__module3__, __module5__);
-
-// handlebars/runtime.js
-var __module6__ = (function(__dependency1__, __dependency2__, __dependency3__) {
- "use strict";
- var __exports__ = {};
- var Utils = __dependency1__;
- var Exception = __dependency2__;
- var COMPILER_REVISION = __dependency3__.COMPILER_REVISION;
- var REVISION_CHANGES = __dependency3__.REVISION_CHANGES;
- var createFrame = __dependency3__.createFrame;
-
- function checkRevision(compilerInfo) {
- var compilerRevision = compilerInfo && compilerInfo[0] || 1,
- currentRevision = COMPILER_REVISION;
-
- if (compilerRevision !== currentRevision) {
- if (compilerRevision < currentRevision) {
- var runtimeVersions = REVISION_CHANGES[currentRevision],
- compilerVersions = REVISION_CHANGES[compilerRevision];
- throw new Exception("Template was precompiled with an older version of Handlebars than the current runtime. "+
- "Please update your precompiler to a newer version ("+runtimeVersions+") or downgrade your runtime to an older version ("+compilerVersions+").");
- } else {
- // Use the embedded version info since the runtime doesn't know about this revision yet
- throw new Exception("Template was precompiled with a newer version of Handlebars than the current runtime. "+
- "Please update your runtime to a newer version ("+compilerInfo[1]+").");
- }
- }
- }
-
- __exports__.checkRevision = checkRevision;// TODO: Remove this line and break up compilePartial
-
- function template(templateSpec, env) {
- /* istanbul ignore next */
- if (!env) {
- throw new Exception("No environment passed to template");
- }
- if (!templateSpec || !templateSpec.main) {
- throw new Exception('Unknown template object: ' + typeof templateSpec);
- }
-
- // Note: Using env.VM references rather than local var references throughout this section to allow
- // for external users to override these as psuedo-supported APIs.
- env.VM.checkRevision(templateSpec.compiler);
-
- var invokePartialWrapper = function(partial, indent, name, context, hash, helpers, partials, data, depths) {
- if (hash) {
- context = Utils.extend({}, context, hash);
- }
-
- var result = env.VM.invokePartial.call(this, partial, name, context, helpers, partials, data, depths);
-
- if (result == null && env.compile) {
- var options = { helpers: helpers, partials: partials, data: data, depths: depths };
- partials[name] = env.compile(partial, { data: data !== undefined, compat: templateSpec.compat }, env);
- result = partials[name](context, options);
- }
- if (result != null) {
- if (indent) {
- var lines = result.split('\n');
- for (var i = 0, l = lines.length; i < l; i++) {
- if (!lines[i] && i + 1 === l) {
- break;
- }
-
- lines[i] = indent + lines[i];
- }
- result = lines.join('\n');
- }
- return result;
- } else {
- throw new Exception("The partial " + name + " could not be compiled when running in runtime-only mode");
- }
- };
-
- // Just add water
- var container = {
- lookup: function(depths, name) {
- var len = depths.length;
- for (var i = 0; i < len; i++) {
- if (depths[i] && depths[i][name] != null) {
- return depths[i][name];
- }
- }
- },
- lambda: function(current, context) {
- return typeof current === 'function' ? current.call(context) : current;
- },
-
- escapeExpression: Utils.escapeExpression,
- invokePartial: invokePartialWrapper,
-
- fn: function(i) {
- return templateSpec[i];
- },
-
- programs: [],
- program: function(i, data, depths) {
- var programWrapper = this.programs[i],
- fn = this.fn(i);
- if (data || depths) {
- programWrapper = program(this, i, fn, data, depths);
- } else if (!programWrapper) {
- programWrapper = this.programs[i] = program(this, i, fn);
- }
- return programWrapper;
- },
-
- data: function(data, depth) {
- while (data && depth--) {
- data = data._parent;
- }
- return data;
- },
- merge: function(param, common) {
- var ret = param || common;
-
- if (param && common && (param !== common)) {
- ret = Utils.extend({}, common, param);
- }
-
- return ret;
- },
-
- noop: env.VM.noop,
- compilerInfo: templateSpec.compiler
- };
-
- var ret = function(context, options) {
- options = options || {};
- var data = options.data;
-
- ret._setup(options);
- if (!options.partial && templateSpec.useData) {
- data = initData(context, data);
- }
- var depths;
- if (templateSpec.useDepths) {
- depths = options.depths ? [context].concat(options.depths) : [context];
- }
-
- return templateSpec.main.call(container, context, container.helpers, container.partials, data, depths);
- };
- ret.isTop = true;
-
- ret._setup = function(options) {
- if (!options.partial) {
- container.helpers = container.merge(options.helpers, env.helpers);
-
- if (templateSpec.usePartial) {
- container.partials = container.merge(options.partials, env.partials);
- }
- } else {
- container.helpers = options.helpers;
- container.partials = options.partials;
- }
- };
-
- ret._child = function(i, data, depths) {
- if (templateSpec.useDepths && !depths) {
- throw new Exception('must pass parent depths');
- }
-
- return program(container, i, templateSpec[i], data, depths);
- };
- return ret;
- }
-
- __exports__.template = template;function program(container, i, fn, data, depths) {
- var prog = function(context, options) {
- options = options || {};
-
- return fn.call(container, context, container.helpers, container.partials, options.data || data, depths && [context].concat(depths));
- };
- prog.program = i;
- prog.depth = depths ? depths.length : 0;
- return prog;
- }
-
- __exports__.program = program;function invokePartial(partial, name, context, helpers, partials, data, depths) {
- var options = { partial: true, helpers: helpers, partials: partials, data: data, depths: depths };
-
- if(partial === undefined) {
- throw new Exception("The partial " + name + " could not be found");
- } else if(partial instanceof Function) {
- return partial(context, options);
- }
- }
-
- __exports__.invokePartial = invokePartial;function noop() { return ""; }
-
- __exports__.noop = noop;function initData(context, data) {
- if (!data || !('root' in data)) {
- data = data ? createFrame(data) : {};
- data.root = context;
- }
- return data;
- }
- return __exports__;
-})(__module3__, __module5__, __module2__);
-
-// handlebars.runtime.js
-var __module1__ = (function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) {
- "use strict";
- var __exports__;
- /*globals Handlebars: true */
- var base = __dependency1__;
-
- // Each of these augment the Handlebars object. No need to setup here.
- // (This is done to easily share code between commonjs and browse envs)
- var SafeString = __dependency2__;
- var Exception = __dependency3__;
- var Utils = __dependency4__;
- var runtime = __dependency5__;
-
- // For compatibility and usage outside of module systems, make the Handlebars object a namespace
- var create = function() {
- var hb = new base.HandlebarsEnvironment();
-
- Utils.extend(hb, base);
- hb.SafeString = SafeString;
- hb.Exception = Exception;
- hb.Utils = Utils;
- hb.escapeExpression = Utils.escapeExpression;
-
- hb.VM = runtime;
- hb.template = function(spec) {
- return runtime.template(spec, hb);
- };
-
- return hb;
- };
-
- var Handlebars = create();
- Handlebars.create = create;
-
- Handlebars['default'] = Handlebars;
-
- __exports__ = Handlebars;
- return __exports__;
-})(__module2__, __module4__, __module5__, __module3__, __module6__);
-
-// handlebars/compiler/ast.js
-var __module7__ = (function(__dependency1__) {
- "use strict";
- var __exports__;
- var Exception = __dependency1__;
-
- function LocationInfo(locInfo) {
- locInfo = locInfo || {};
- this.firstLine = locInfo.first_line;
- this.firstColumn = locInfo.first_column;
- this.lastColumn = locInfo.last_column;
- this.lastLine = locInfo.last_line;
- }
-
- var AST = {
- ProgramNode: function(statements, strip, locInfo) {
- LocationInfo.call(this, locInfo);
- this.type = "program";
- this.statements = statements;
- this.strip = strip;
- },
-
- MustacheNode: function(rawParams, hash, open, strip, locInfo) {
- LocationInfo.call(this, locInfo);
- this.type = "mustache";
- this.strip = strip;
-
- // Open may be a string parsed from the parser or a passed boolean flag
- if (open != null && open.charAt) {
- // Must use charAt to support IE pre-10
- var escapeFlag = open.charAt(3) || open.charAt(2);
- this.escaped = escapeFlag !== '{' && escapeFlag !== '&';
- } else {
- this.escaped = !!open;
- }
-
- if (rawParams instanceof AST.SexprNode) {
- this.sexpr = rawParams;
- } else {
- // Support old AST API
- this.sexpr = new AST.SexprNode(rawParams, hash);
- }
-
- // Support old AST API that stored this info in MustacheNode
- this.id = this.sexpr.id;
- this.params = this.sexpr.params;
- this.hash = this.sexpr.hash;
- this.eligibleHelper = this.sexpr.eligibleHelper;
- this.isHelper = this.sexpr.isHelper;
- },
-
- SexprNode: function(rawParams, hash, locInfo) {
- LocationInfo.call(this, locInfo);
-
- this.type = "sexpr";
- this.hash = hash;
-
- var id = this.id = rawParams[0];
- var params = this.params = rawParams.slice(1);
-
- // a mustache is definitely a helper if:
- // * it is an eligible helper, and
- // * it has at least one parameter or hash segment
- this.isHelper = !!(params.length || hash);
-
- // a mustache is an eligible helper if:
- // * its id is simple (a single part, not `this` or `..`)
- this.eligibleHelper = this.isHelper || id.isSimple;
-
- // if a mustache is an eligible helper but not a definite
- // helper, it is ambiguous, and will be resolved in a later
- // pass or at runtime.
- },
-
- PartialNode: function(partialName, context, hash, strip, locInfo) {
- LocationInfo.call(this, locInfo);
- this.type = "partial";
- this.partialName = partialName;
- this.context = context;
- this.hash = hash;
- this.strip = strip;
-
- this.strip.inlineStandalone = true;
- },
-
- BlockNode: function(mustache, program, inverse, strip, locInfo) {
- LocationInfo.call(this, locInfo);
-
- this.type = 'block';
- this.mustache = mustache;
- this.program = program;
- this.inverse = inverse;
- this.strip = strip;
-
- if (inverse && !program) {
- this.isInverse = true;
- }
- },
-
- RawBlockNode: function(mustache, content, close, locInfo) {
- LocationInfo.call(this, locInfo);
-
- if (mustache.sexpr.id.original !== close) {
- throw new Exception(mustache.sexpr.id.original + " doesn't match " + close, this);
- }
-
- content = new AST.ContentNode(content, locInfo);
-
- this.type = 'block';
- this.mustache = mustache;
- this.program = new AST.ProgramNode([content], {}, locInfo);
- },
-
- ContentNode: function(string, locInfo) {
- LocationInfo.call(this, locInfo);
- this.type = "content";
- this.original = this.string = string;
- },
-
- HashNode: function(pairs, locInfo) {
- LocationInfo.call(this, locInfo);
- this.type = "hash";
- this.pairs = pairs;
- },
-
- IdNode: function(parts, locInfo) {
- LocationInfo.call(this, locInfo);
- this.type = "ID";
-
- var original = "",
- dig = [],
- depth = 0,
- depthString = '';
-
- for(var i=0,l=parts.length; i 0) {
- throw new Exception("Invalid path: " + original, this);
- } else if (part === "..") {
- depth++;
- depthString += '../';
- } else {
- this.isScoped = true;
- }
- } else {
- dig.push(part);
- }
- }
-
- this.original = original;
- this.parts = dig;
- this.string = dig.join('.');
- this.depth = depth;
- this.idName = depthString + this.string;
-
- // an ID is simple if it only has one part, and that part is not
- // `..` or `this`.
- this.isSimple = parts.length === 1 && !this.isScoped && depth === 0;
-
- this.stringModeValue = this.string;
- },
-
- PartialNameNode: function(name, locInfo) {
- LocationInfo.call(this, locInfo);
- this.type = "PARTIAL_NAME";
- this.name = name.original;
- },
-
- DataNode: function(id, locInfo) {
- LocationInfo.call(this, locInfo);
- this.type = "DATA";
- this.id = id;
- this.stringModeValue = id.stringModeValue;
- this.idName = '@' + id.stringModeValue;
- },
-
- StringNode: function(string, locInfo) {
- LocationInfo.call(this, locInfo);
- this.type = "STRING";
- this.original =
- this.string =
- this.stringModeValue = string;
- },
-
- NumberNode: function(number, locInfo) {
- LocationInfo.call(this, locInfo);
- this.type = "NUMBER";
- this.original =
- this.number = number;
- this.stringModeValue = Number(number);
- },
-
- BooleanNode: function(bool, locInfo) {
- LocationInfo.call(this, locInfo);
- this.type = "BOOLEAN";
- this.bool = bool;
- this.stringModeValue = bool === "true";
- },
-
- CommentNode: function(comment, locInfo) {
- LocationInfo.call(this, locInfo);
- this.type = "comment";
- this.comment = comment;
-
- this.strip = {
- inlineStandalone: true
- };
- }
- };
-
-
- // Must be exported as an object rather than the root of the module as the jison lexer
- // most modify the object to operate properly.
- __exports__ = AST;
- return __exports__;
-})(__module5__);
-
-// handlebars/compiler/parser.js
-var __module9__ = (function() {
- "use strict";
- var __exports__;
- /* jshint ignore:start */
- /* istanbul ignore next */
- /* Jison generated parser */
- var handlebars = (function(){
- var parser = {trace: function trace() { },
- yy: {},
- symbols_: {"error":2,"root":3,"program":4,"EOF":5,"program_repetition0":6,"statement":7,"mustache":8,"block":9,"rawBlock":10,"partial":11,"CONTENT":12,"COMMENT":13,"openRawBlock":14,"END_RAW_BLOCK":15,"OPEN_RAW_BLOCK":16,"sexpr":17,"CLOSE_RAW_BLOCK":18,"openBlock":19,"block_option0":20,"closeBlock":21,"openInverse":22,"block_option1":23,"OPEN_BLOCK":24,"CLOSE":25,"OPEN_INVERSE":26,"inverseAndProgram":27,"INVERSE":28,"OPEN_ENDBLOCK":29,"path":30,"OPEN":31,"OPEN_UNESCAPED":32,"CLOSE_UNESCAPED":33,"OPEN_PARTIAL":34,"partialName":35,"param":36,"partial_option0":37,"partial_option1":38,"sexpr_repetition0":39,"sexpr_option0":40,"dataName":41,"STRING":42,"NUMBER":43,"BOOLEAN":44,"OPEN_SEXPR":45,"CLOSE_SEXPR":46,"hash":47,"hash_repetition_plus0":48,"hashSegment":49,"ID":50,"EQUALS":51,"DATA":52,"pathSegments":53,"SEP":54,"$accept":0,"$end":1},
- terminals_: {2:"error",5:"EOF",12:"CONTENT",13:"COMMENT",15:"END_RAW_BLOCK",16:"OPEN_RAW_BLOCK",18:"CLOSE_RAW_BLOCK",24:"OPEN_BLOCK",25:"CLOSE",26:"OPEN_INVERSE",28:"INVERSE",29:"OPEN_ENDBLOCK",31:"OPEN",32:"OPEN_UNESCAPED",33:"CLOSE_UNESCAPED",34:"OPEN_PARTIAL",42:"STRING",43:"NUMBER",44:"BOOLEAN",45:"OPEN_SEXPR",46:"CLOSE_SEXPR",50:"ID",51:"EQUALS",52:"DATA",54:"SEP"},
- productions_: [0,[3,2],[4,1],[7,1],[7,1],[7,1],[7,1],[7,1],[7,1],[10,3],[14,3],[9,4],[9,4],[19,3],[22,3],[27,2],[21,3],[8,3],[8,3],[11,5],[11,4],[17,3],[17,1],[36,1],[36,1],[36,1],[36,1],[36,1],[36,3],[47,1],[49,3],[35,1],[35,1],[35,1],[41,2],[30,1],[53,3],[53,1],[6,0],[6,2],[20,0],[20,1],[23,0],[23,1],[37,0],[37,1],[38,0],[38,1],[39,0],[39,2],[40,0],[40,1],[48,1],[48,2]],
- performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
-
- var $0 = $$.length - 1;
- switch (yystate) {
- case 1: yy.prepareProgram($$[$0-1].statements, true); return $$[$0-1];
- break;
- case 2:this.$ = new yy.ProgramNode(yy.prepareProgram($$[$0]), {}, this._$);
- break;
- case 3:this.$ = $$[$0];
- break;
- case 4:this.$ = $$[$0];
- break;
- case 5:this.$ = $$[$0];
- break;
- case 6:this.$ = $$[$0];
- break;
- case 7:this.$ = new yy.ContentNode($$[$0], this._$);
- break;
- case 8:this.$ = new yy.CommentNode($$[$0], this._$);
- break;
- case 9:this.$ = new yy.RawBlockNode($$[$0-2], $$[$0-1], $$[$0], this._$);
- break;
- case 10:this.$ = new yy.MustacheNode($$[$0-1], null, '', '', this._$);
- break;
- case 11:this.$ = yy.prepareBlock($$[$0-3], $$[$0-2], $$[$0-1], $$[$0], false, this._$);
- break;
- case 12:this.$ = yy.prepareBlock($$[$0-3], $$[$0-2], $$[$0-1], $$[$0], true, this._$);
- break;
- case 13:this.$ = new yy.MustacheNode($$[$0-1], null, $$[$0-2], yy.stripFlags($$[$0-2], $$[$0]), this._$);
- break;
- case 14:this.$ = new yy.MustacheNode($$[$0-1], null, $$[$0-2], yy.stripFlags($$[$0-2], $$[$0]), this._$);
- break;
- case 15:this.$ = { strip: yy.stripFlags($$[$0-1], $$[$0-1]), program: $$[$0] };
- break;
- case 16:this.$ = {path: $$[$0-1], strip: yy.stripFlags($$[$0-2], $$[$0])};
- break;
- case 17:this.$ = new yy.MustacheNode($$[$0-1], null, $$[$0-2], yy.stripFlags($$[$0-2], $$[$0]), this._$);
- break;
- case 18:this.$ = new yy.MustacheNode($$[$0-1], null, $$[$0-2], yy.stripFlags($$[$0-2], $$[$0]), this._$);
- break;
- case 19:this.$ = new yy.PartialNode($$[$0-3], $$[$0-2], $$[$0-1], yy.stripFlags($$[$0-4], $$[$0]), this._$);
- break;
- case 20:this.$ = new yy.PartialNode($$[$0-2], undefined, $$[$0-1], yy.stripFlags($$[$0-3], $$[$0]), this._$);
- break;
- case 21:this.$ = new yy.SexprNode([$$[$0-2]].concat($$[$0-1]), $$[$0], this._$);
- break;
- case 22:this.$ = new yy.SexprNode([$$[$0]], null, this._$);
- break;
- case 23:this.$ = $$[$0];
- break;
- case 24:this.$ = new yy.StringNode($$[$0], this._$);
- break;
- case 25:this.$ = new yy.NumberNode($$[$0], this._$);
- break;
- case 26:this.$ = new yy.BooleanNode($$[$0], this._$);
- break;
- case 27:this.$ = $$[$0];
- break;
- case 28:$$[$0-1].isHelper = true; this.$ = $$[$0-1];
- break;
- case 29:this.$ = new yy.HashNode($$[$0], this._$);
- break;
- case 30:this.$ = [$$[$0-2], $$[$0]];
- break;
- case 31:this.$ = new yy.PartialNameNode($$[$0], this._$);
- break;
- case 32:this.$ = new yy.PartialNameNode(new yy.StringNode($$[$0], this._$), this._$);
- break;
- case 33:this.$ = new yy.PartialNameNode(new yy.NumberNode($$[$0], this._$));
- break;
- case 34:this.$ = new yy.DataNode($$[$0], this._$);
- break;
- case 35:this.$ = new yy.IdNode($$[$0], this._$);
- break;
- case 36: $$[$0-2].push({part: $$[$0], separator: $$[$0-1]}); this.$ = $$[$0-2];
- break;
- case 37:this.$ = [{part: $$[$0]}];
- break;
- case 38:this.$ = [];
- break;
- case 39:$$[$0-1].push($$[$0]);
- break;
- case 48:this.$ = [];
- break;
- case 49:$$[$0-1].push($$[$0]);
- break;
- case 52:this.$ = [$$[$0]];
- break;
- case 53:$$[$0-1].push($$[$0]);
- break;
- }
- },
- table: [{3:1,4:2,5:[2,38],6:3,12:[2,38],13:[2,38],16:[2,38],24:[2,38],26:[2,38],31:[2,38],32:[2,38],34:[2,38]},{1:[3]},{5:[1,4]},{5:[2,2],7:5,8:6,9:7,10:8,11:9,12:[1,10],13:[1,11],14:16,16:[1,20],19:14,22:15,24:[1,18],26:[1,19],28:[2,2],29:[2,2],31:[1,12],32:[1,13],34:[1,17]},{1:[2,1]},{5:[2,39],12:[2,39],13:[2,39],16:[2,39],24:[2,39],26:[2,39],28:[2,39],29:[2,39],31:[2,39],32:[2,39],34:[2,39]},{5:[2,3],12:[2,3],13:[2,3],16:[2,3],24:[2,3],26:[2,3],28:[2,3],29:[2,3],31:[2,3],32:[2,3],34:[2,3]},{5:[2,4],12:[2,4],13:[2,4],16:[2,4],24:[2,4],26:[2,4],28:[2,4],29:[2,4],31:[2,4],32:[2,4],34:[2,4]},{5:[2,5],12:[2,5],13:[2,5],16:[2,5],24:[2,5],26:[2,5],28:[2,5],29:[2,5],31:[2,5],32:[2,5],34:[2,5]},{5:[2,6],12:[2,6],13:[2,6],16:[2,6],24:[2,6],26:[2,6],28:[2,6],29:[2,6],31:[2,6],32:[2,6],34:[2,6]},{5:[2,7],12:[2,7],13:[2,7],16:[2,7],24:[2,7],26:[2,7],28:[2,7],29:[2,7],31:[2,7],32:[2,7],34:[2,7]},{5:[2,8],12:[2,8],13:[2,8],16:[2,8],24:[2,8],26:[2,8],28:[2,8],29:[2,8],31:[2,8],32:[2,8],34:[2,8]},{17:21,30:22,41:23,50:[1,26],52:[1,25],53:24},{17:27,30:22,41:23,50:[1,26],52:[1,25],53:24},{4:28,6:3,12:[2,38],13:[2,38],16:[2,38],24:[2,38],26:[2,38],28:[2,38],29:[2,38],31:[2,38],32:[2,38],34:[2,38]},{4:29,6:3,12:[2,38],13:[2,38],16:[2,38],24:[2,38],26:[2,38],28:[2,38],29:[2,38],31:[2,38],32:[2,38],34:[2,38]},{12:[1,30]},{30:32,35:31,42:[1,33],43:[1,34],50:[1,26],53:24},{17:35,30:22,41:23,50:[1,26],52:[1,25],53:24},{17:36,30:22,41:23,50:[1,26],52:[1,25],53:24},{17:37,30:22,41:23,50:[1,26],52:[1,25],53:24},{25:[1,38]},{18:[2,48],25:[2,48],33:[2,48],39:39,42:[2,48],43:[2,48],44:[2,48],45:[2,48],46:[2,48],50:[2,48],52:[2,48]},{18:[2,22],25:[2,22],33:[2,22],46:[2,22]},{18:[2,35],25:[2,35],33:[2,35],42:[2,35],43:[2,35],44:[2,35],45:[2,35],46:[2,35],50:[2,35],52:[2,35],54:[1,40]},{30:41,50:[1,26],53:24},{18:[2,37],25:[2,37],33:[2,37],42:[2,37],43:[2,37],44:[2,37],45:[2,37],46:[2,37],50:[2,37],52:[2,37],54:[2,37]},{33:[1,42]},{20:43,27:44,28:[1,45],29:[2,40]},{23:46,27:47,28:[1,45],29:[2,42]},{15:[1,48]},{25:[2,46],30:51,36:49,38:50,41:55,42:[1,52],43:[1,53],44:[1,54],45:[1,56],47:57,48:58,49:60,50:[1,59],52:[1,25],53:24},{25:[2,31],42:[2,31],43:[2,31],44:[2,31],45:[2,31],50:[2,31],52:[2,31]},{25:[2,32],42:[2,32],43:[2,32],44:[2,32],45:[2,32],50:[2,32],52:[2,32]},{25:[2,33],42:[2,33],43:[2,33],44:[2,33],45:[2,33],50:[2,33],52:[2,33]},{25:[1,61]},{25:[1,62]},{18:[1,63]},{5:[2,17],12:[2,17],13:[2,17],16:[2,17],24:[2,17],26:[2,17],28:[2,17],29:[2,17],31:[2,17],32:[2,17],34:[2,17]},{18:[2,50],25:[2,50],30:51,33:[2,50],36:65,40:64,41:55,42:[1,52],43:[1,53],44:[1,54],45:[1,56],46:[2,50],47:66,48:58,49:60,50:[1,59],52:[1,25],53:24},{50:[1,67]},{18:[2,34],25:[2,34],33:[2,34],42:[2,34],43:[2,34],44:[2,34],45:[2,34],46:[2,34],50:[2,34],52:[2,34]},{5:[2,18],12:[2,18],13:[2,18],16:[2,18],24:[2,18],26:[2,18],28:[2,18],29:[2,18],31:[2,18],32:[2,18],34:[2,18]},{21:68,29:[1,69]},{29:[2,41]},{4:70,6:3,12:[2,38],13:[2,38],16:[2,38],24:[2,38],26:[2,38],29:[2,38],31:[2,38],32:[2,38],34:[2,38]},{21:71,29:[1,69]},{29:[2,43]},{5:[2,9],12:[2,9],13:[2,9],16:[2,9],24:[2,9],26:[2,9],28:[2,9],29:[2,9],31:[2,9],32:[2,9],34:[2,9]},{25:[2,44],37:72,47:73,48:58,49:60,50:[1,74]},{25:[1,75]},{18:[2,23],25:[2,23],33:[2,23],42:[2,23],43:[2,23],44:[2,23],45:[2,23],46:[2,23],50:[2,23],52:[2,23]},{18:[2,24],25:[2,24],33:[2,24],42:[2,24],43:[2,24],44:[2,24],45:[2,24],46:[2,24],50:[2,24],52:[2,24]},{18:[2,25],25:[2,25],33:[2,25],42:[2,25],43:[2,25],44:[2,25],45:[2,25],46:[2,25],50:[2,25],52:[2,25]},{18:[2,26],25:[2,26],33:[2,26],42:[2,26],43:[2,26],44:[2,26],45:[2,26],46:[2,26],50:[2,26],52:[2,26]},{18:[2,27],25:[2,27],33:[2,27],42:[2,27],43:[2,27],44:[2,27],45:[2,27],46:[2,27],50:[2,27],52:[2,27]},{17:76,30:22,41:23,50:[1,26],52:[1,25],53:24},{25:[2,47]},{18:[2,29],25:[2,29],33:[2,29],46:[2,29],49:77,50:[1,74]},{18:[2,37],25:[2,37],33:[2,37],42:[2,37],43:[2,37],44:[2,37],45:[2,37],46:[2,37],50:[2,37],51:[1,78],52:[2,37],54:[2,37]},{18:[2,52],25:[2,52],33:[2,52],46:[2,52],50:[2,52]},{12:[2,13],13:[2,13],16:[2,13],24:[2,13],26:[2,13],28:[2,13],29:[2,13],31:[2,13],32:[2,13],34:[2,13]},{12:[2,14],13:[2,14],16:[2,14],24:[2,14],26:[2,14],28:[2,14],29:[2,14],31:[2,14],32:[2,14],34:[2,14]},{12:[2,10]},{18:[2,21],25:[2,21],33:[2,21],46:[2,21]},{18:[2,49],25:[2,49],33:[2,49],42:[2,49],43:[2,49],44:[2,49],45:[2,49],46:[2,49],50:[2,49],52:[2,49]},{18:[2,51],25:[2,51],33:[2,51],46:[2,51]},{18:[2,36],25:[2,36],33:[2,36],42:[2,36],43:[2,36],44:[2,36],45:[2,36],46:[2,36],50:[2,36],52:[2,36],54:[2,36]},{5:[2,11],12:[2,11],13:[2,11],16:[2,11],24:[2,11],26:[2,11],28:[2,11],29:[2,11],31:[2,11],32:[2,11],34:[2,11]},{30:79,50:[1,26],53:24},{29:[2,15]},{5:[2,12],12:[2,12],13:[2,12],16:[2,12],24:[2,12],26:[2,12],28:[2,12],29:[2,12],31:[2,12],32:[2,12],34:[2,12]},{25:[1,80]},{25:[2,45]},{51:[1,78]},{5:[2,20],12:[2,20],13:[2,20],16:[2,20],24:[2,20],26:[2,20],28:[2,20],29:[2,20],31:[2,20],32:[2,20],34:[2,20]},{46:[1,81]},{18:[2,53],25:[2,53],33:[2,53],46:[2,53],50:[2,53]},{30:51,36:82,41:55,42:[1,52],43:[1,53],44:[1,54],45:[1,56],50:[1,26],52:[1,25],53:24},{25:[1,83]},{5:[2,19],12:[2,19],13:[2,19],16:[2,19],24:[2,19],26:[2,19],28:[2,19],29:[2,19],31:[2,19],32:[2,19],34:[2,19]},{18:[2,28],25:[2,28],33:[2,28],42:[2,28],43:[2,28],44:[2,28],45:[2,28],46:[2,28],50:[2,28],52:[2,28]},{18:[2,30],25:[2,30],33:[2,30],46:[2,30],50:[2,30]},{5:[2,16],12:[2,16],13:[2,16],16:[2,16],24:[2,16],26:[2,16],28:[2,16],29:[2,16],31:[2,16],32:[2,16],34:[2,16]}],
- defaultActions: {4:[2,1],44:[2,41],47:[2,43],57:[2,47],63:[2,10],70:[2,15],73:[2,45]},
- parseError: function parseError(str, hash) {
- throw new Error(str);
- },
- parse: function parse(input) {
- var self = this, stack = [0], vstack = [null], lstack = [], table = this.table, yytext = "", yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1;
- this.lexer.setInput(input);
- this.lexer.yy = this.yy;
- this.yy.lexer = this.lexer;
- this.yy.parser = this;
- if (typeof this.lexer.yylloc == "undefined")
- this.lexer.yylloc = {};
- var yyloc = this.lexer.yylloc;
- lstack.push(yyloc);
- var ranges = this.lexer.options && this.lexer.options.ranges;
- if (typeof this.yy.parseError === "function")
- this.parseError = this.yy.parseError;
- function popStack(n) {
- stack.length = stack.length - 2 * n;
- vstack.length = vstack.length - n;
- lstack.length = lstack.length - n;
- }
- function lex() {
- var token;
- token = self.lexer.lex() || 1;
- if (typeof token !== "number") {
- token = self.symbols_[token] || token;
- }
- return token;
- }
- var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected;
- while (true) {
- state = stack[stack.length - 1];
- if (this.defaultActions[state]) {
- action = this.defaultActions[state];
- } else {
- if (symbol === null || typeof symbol == "undefined") {
- symbol = lex();
- }
- action = table[state] && table[state][symbol];
- }
- if (typeof action === "undefined" || !action.length || !action[0]) {
- var errStr = "";
- if (!recovering) {
- expected = [];
- for (p in table[state])
- if (this.terminals_[p] && p > 2) {
- expected.push("'" + this.terminals_[p] + "'");
- }
- if (this.lexer.showPosition) {
- errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'";
- } else {
- errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'");
- }
- this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected});
- }
- }
- if (action[0] instanceof Array && action.length > 1) {
- throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol);
- }
- switch (action[0]) {
- case 1:
- stack.push(symbol);
- vstack.push(this.lexer.yytext);
- lstack.push(this.lexer.yylloc);
- stack.push(action[1]);
- symbol = null;
- if (!preErrorSymbol) {
- yyleng = this.lexer.yyleng;
- yytext = this.lexer.yytext;
- yylineno = this.lexer.yylineno;
- yyloc = this.lexer.yylloc;
- if (recovering > 0)
- recovering--;
- } else {
- symbol = preErrorSymbol;
- preErrorSymbol = null;
- }
- break;
- case 2:
- len = this.productions_[action[1]][1];
- yyval.$ = vstack[vstack.length - len];
- yyval._$ = {first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column};
- if (ranges) {
- yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]];
- }
- r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
- if (typeof r !== "undefined") {
- return r;
- }
- if (len) {
- stack = stack.slice(0, -1 * len * 2);
- vstack = vstack.slice(0, -1 * len);
- lstack = lstack.slice(0, -1 * len);
- }
- stack.push(this.productions_[action[1]][0]);
- vstack.push(yyval.$);
- lstack.push(yyval._$);
- newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
- stack.push(newState);
- break;
- case 3:
- return true;
- }
- }
- return true;
- }
- };
- /* Jison generated lexer */
- var lexer = (function(){
- var lexer = ({EOF:1,
- parseError:function parseError(str, hash) {
- if (this.yy.parser) {
- this.yy.parser.parseError(str, hash);
- } else {
- throw new Error(str);
- }
- },
- setInput:function (input) {
- this._input = input;
- this._more = this._less = this.done = false;
- this.yylineno = this.yyleng = 0;
- this.yytext = this.matched = this.match = '';
- this.conditionStack = ['INITIAL'];
- this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0};
- if (this.options.ranges) this.yylloc.range = [0,0];
- this.offset = 0;
- return this;
- },
- input:function () {
- var ch = this._input[0];
- this.yytext += ch;
- this.yyleng++;
- this.offset++;
- this.match += ch;
- this.matched += ch;
- var lines = ch.match(/(?:\r\n?|\n).*/g);
- if (lines) {
- this.yylineno++;
- this.yylloc.last_line++;
- } else {
- this.yylloc.last_column++;
- }
- if (this.options.ranges) this.yylloc.range[1]++;
-
- this._input = this._input.slice(1);
- return ch;
- },
- unput:function (ch) {
- var len = ch.length;
- var lines = ch.split(/(?:\r\n?|\n)/g);
-
- this._input = ch + this._input;
- this.yytext = this.yytext.substr(0, this.yytext.length-len-1);
- //this.yyleng -= len;
- this.offset -= len;
- var oldLines = this.match.split(/(?:\r\n?|\n)/g);
- this.match = this.match.substr(0, this.match.length-1);
- this.matched = this.matched.substr(0, this.matched.length-1);
-
- if (lines.length-1) this.yylineno -= lines.length-1;
- var r = this.yylloc.range;
-
- this.yylloc = {first_line: this.yylloc.first_line,
- last_line: this.yylineno+1,
- first_column: this.yylloc.first_column,
- last_column: lines ?
- (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length:
- this.yylloc.first_column - len
- };
-
- if (this.options.ranges) {
- this.yylloc.range = [r[0], r[0] + this.yyleng - len];
- }
- return this;
- },
- more:function () {
- this._more = true;
- return this;
- },
- less:function (n) {
- this.unput(this.match.slice(n));
- },
- pastInput:function () {
- var past = this.matched.substr(0, this.matched.length - this.match.length);
- return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
- },
- upcomingInput:function () {
- var next = this.match;
- if (next.length < 20) {
- next += this._input.substr(0, 20-next.length);
- }
- return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, "");
- },
- showPosition:function () {
- var pre = this.pastInput();
- var c = new Array(pre.length + 1).join("-");
- return pre + this.upcomingInput() + "\n" + c+"^";
- },
- next:function () {
- if (this.done) {
- return this.EOF;
- }
- if (!this._input) this.done = true;
-
- var token,
- match,
- tempMatch,
- index,
- col,
- lines;
- if (!this._more) {
- this.yytext = '';
- this.match = '';
- }
- var rules = this._currentRules();
- for (var i=0;i < rules.length; i++) {
- tempMatch = this._input.match(this.rules[rules[i]]);
- if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
- match = tempMatch;
- index = i;
- if (!this.options.flex) break;
- }
- }
- if (match) {
- lines = match[0].match(/(?:\r\n?|\n).*/g);
- if (lines) this.yylineno += lines.length;
- this.yylloc = {first_line: this.yylloc.last_line,
- last_line: this.yylineno+1,
- first_column: this.yylloc.last_column,
- last_column: lines ? lines[lines.length-1].length-lines[lines.length-1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length};
- this.yytext += match[0];
- this.match += match[0];
- this.matches = match;
- this.yyleng = this.yytext.length;
- if (this.options.ranges) {
- this.yylloc.range = [this.offset, this.offset += this.yyleng];
- }
- this._more = false;
- this._input = this._input.slice(match[0].length);
- this.matched += match[0];
- token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]);
- if (this.done && this._input) this.done = false;
- if (token) return token;
- else return;
- }
- if (this._input === "") {
- return this.EOF;
- } else {
- return this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(),
- {text: "", token: null, line: this.yylineno});
- }
- },
- lex:function lex() {
- var r = this.next();
- if (typeof r !== 'undefined') {
- return r;
- } else {
- return this.lex();
- }
- },
- begin:function begin(condition) {
- this.conditionStack.push(condition);
- },
- popState:function popState() {
- return this.conditionStack.pop();
- },
- _currentRules:function _currentRules() {
- return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules;
- },
- topState:function () {
- return this.conditionStack[this.conditionStack.length-2];
- },
- pushState:function begin(condition) {
- this.begin(condition);
- }});
- lexer.options = {};
- lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
-
-
- function strip(start, end) {
- return yy_.yytext = yy_.yytext.substr(start, yy_.yyleng-end);
- }
-
-
- var YYSTATE=YY_START
- switch($avoiding_name_collisions) {
- case 0:
- if(yy_.yytext.slice(-2) === "\\\\") {
- strip(0,1);
- this.begin("mu");
- } else if(yy_.yytext.slice(-1) === "\\") {
- strip(0,1);
- this.begin("emu");
- } else {
- this.begin("mu");
- }
- if(yy_.yytext) return 12;
-
- break;
- case 1:return 12;
- break;
- case 2:
- this.popState();
- return 12;
-
- break;
- case 3:
- yy_.yytext = yy_.yytext.substr(5, yy_.yyleng-9);
- this.popState();
- return 15;
-
- break;
- case 4: return 12;
- break;
- case 5:strip(0,4); this.popState(); return 13;
- break;
- case 6:return 45;
- break;
- case 7:return 46;
- break;
- case 8: return 16;
- break;
- case 9:
- this.popState();
- this.begin('raw');
- return 18;
-
- break;
- case 10:return 34;
- break;
- case 11:return 24;
- break;
- case 12:return 29;
- break;
- case 13:this.popState(); return 28;
- break;
- case 14:this.popState(); return 28;
- break;
- case 15:return 26;
- break;
- case 16:return 26;
- break;
- case 17:return 32;
- break;
- case 18:return 31;
- break;
- case 19:this.popState(); this.begin('com');
- break;
- case 20:strip(3,5); this.popState(); return 13;
- break;
- case 21:return 31;
- break;
- case 22:return 51;
- break;
- case 23:return 50;
- break;
- case 24:return 50;
- break;
- case 25:return 54;
- break;
- case 26:// ignore whitespace
- break;
- case 27:this.popState(); return 33;
- break;
- case 28:this.popState(); return 25;
- break;
- case 29:yy_.yytext = strip(1,2).replace(/\\"/g,'"'); return 42;
- break;
- case 30:yy_.yytext = strip(1,2).replace(/\\'/g,"'"); return 42;
- break;
- case 31:return 52;
- break;
- case 32:return 44;
- break;
- case 33:return 44;
- break;
- case 34:return 43;
- break;
- case 35:return 50;
- break;
- case 36:yy_.yytext = strip(1,2); return 50;
- break;
- case 37:return 'INVALID';
- break;
- case 38:return 5;
- break;
- }
- };
- lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/,/^(?:\{\{\{\{\/[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.])\}\}\}\})/,/^(?:[^\x00]*?(?=(\{\{\{\{\/)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\()/,/^(?:\))/,/^(?:\{\{\{\{)/,/^(?:\}\}\}\})/,/^(?:\{\{(~)?>)/,/^(?:\{\{(~)?#)/,/^(?:\{\{(~)?\/)/,/^(?:\{\{(~)?\^\s*(~)?\}\})/,/^(?:\{\{(~)?\s*else\s*(~)?\}\})/,/^(?:\{\{(~)?\^)/,/^(?:\{\{(~)?\s*else\b)/,/^(?:\{\{(~)?\{)/,/^(?:\{\{(~)?&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{(~)?)/,/^(?:=)/,/^(?:\.\.)/,/^(?:\.(?=([=~}\s\/.)])))/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}(~)?\}\})/,/^(?:(~)?\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=([~}\s)])))/,/^(?:false(?=([~}\s)])))/,/^(?:-?[0-9]+(?:\.[0-9]+)?(?=([~}\s)])))/,/^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)]))))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/];
- lexer.conditions = {"mu":{"rules":[6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"com":{"rules":[5],"inclusive":false},"raw":{"rules":[3,4],"inclusive":false},"INITIAL":{"rules":[0,1,38],"inclusive":true}};
- return lexer;})()
- parser.lexer = lexer;
- function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser;
- return new Parser;
- })();__exports__ = handlebars;
- /* jshint ignore:end */
- return __exports__;
-})();
-
-// handlebars/compiler/helpers.js
-var __module10__ = (function(__dependency1__) {
- "use strict";
- var __exports__ = {};
- var Exception = __dependency1__;
-
- function stripFlags(open, close) {
- return {
- left: open.charAt(2) === '~',
- right: close.charAt(close.length-3) === '~'
- };
- }
-
- __exports__.stripFlags = stripFlags;
- function prepareBlock(mustache, program, inverseAndProgram, close, inverted, locInfo) {
- /*jshint -W040 */
- if (mustache.sexpr.id.original !== close.path.original) {
- throw new Exception(mustache.sexpr.id.original + ' doesn\'t match ' + close.path.original, mustache);
- }
-
- var inverse = inverseAndProgram && inverseAndProgram.program;
-
- var strip = {
- left: mustache.strip.left,
- right: close.strip.right,
-
- // Determine the standalone candiacy. Basically flag our content as being possibly standalone
- // so our parent can determine if we actually are standalone
- openStandalone: isNextWhitespace(program.statements),
- closeStandalone: isPrevWhitespace((inverse || program).statements)
- };
-
- if (mustache.strip.right) {
- omitRight(program.statements, null, true);
- }
-
- if (inverse) {
- var inverseStrip = inverseAndProgram.strip;
-
- if (inverseStrip.left) {
- omitLeft(program.statements, null, true);
- }
- if (inverseStrip.right) {
- omitRight(inverse.statements, null, true);
- }
- if (close.strip.left) {
- omitLeft(inverse.statements, null, true);
- }
-
- // Find standalone else statments
- if (isPrevWhitespace(program.statements)
- && isNextWhitespace(inverse.statements)) {
-
- omitLeft(program.statements);
- omitRight(inverse.statements);
- }
- } else {
- if (close.strip.left) {
- omitLeft(program.statements, null, true);
- }
- }
-
- if (inverted) {
- return new this.BlockNode(mustache, inverse, program, strip, locInfo);
- } else {
- return new this.BlockNode(mustache, program, inverse, strip, locInfo);
- }
- }
-
- __exports__.prepareBlock = prepareBlock;
- function prepareProgram(statements, isRoot) {
- for (var i = 0, l = statements.length; i < l; i++) {
- var current = statements[i],
- strip = current.strip;
-
- if (!strip) {
- continue;
- }
-
- var _isPrevWhitespace = isPrevWhitespace(statements, i, isRoot, current.type === 'partial'),
- _isNextWhitespace = isNextWhitespace(statements, i, isRoot),
-
- openStandalone = strip.openStandalone && _isPrevWhitespace,
- closeStandalone = strip.closeStandalone && _isNextWhitespace,
- inlineStandalone = strip.inlineStandalone && _isPrevWhitespace && _isNextWhitespace;
-
- if (strip.right) {
- omitRight(statements, i, true);
- }
- if (strip.left) {
- omitLeft(statements, i, true);
- }
-
- if (inlineStandalone) {
- omitRight(statements, i);
-
- if (omitLeft(statements, i)) {
- // If we are on a standalone node, save the indent info for partials
- if (current.type === 'partial') {
- current.indent = (/([ \t]+$)/).exec(statements[i-1].original) ? RegExp.$1 : '';
- }
- }
- }
- if (openStandalone) {
- omitRight((current.program || current.inverse).statements);
-
- // Strip out the previous content node if it's whitespace only
- omitLeft(statements, i);
- }
- if (closeStandalone) {
- // Always strip the next node
- omitRight(statements, i);
-
- omitLeft((current.inverse || current.program).statements);
- }
- }
-
- return statements;
- }
-
- __exports__.prepareProgram = prepareProgram;function isPrevWhitespace(statements, i, isRoot) {
- if (i === undefined) {
- i = statements.length;
- }
-
- // Nodes that end with newlines are considered whitespace (but are special
- // cased for strip operations)
- var prev = statements[i-1],
- sibling = statements[i-2];
- if (!prev) {
- return isRoot;
- }
-
- if (prev.type === 'content') {
- return (sibling || !isRoot ? (/\r?\n\s*?$/) : (/(^|\r?\n)\s*?$/)).test(prev.original);
- }
- }
- function isNextWhitespace(statements, i, isRoot) {
- if (i === undefined) {
- i = -1;
- }
-
- var next = statements[i+1],
- sibling = statements[i+2];
- if (!next) {
- return isRoot;
- }
-
- if (next.type === 'content') {
- return (sibling || !isRoot ? (/^\s*?\r?\n/) : (/^\s*?(\r?\n|$)/)).test(next.original);
- }
- }
-
- // Marks the node to the right of the position as omitted.
- // I.e. {{foo}}' ' will mark the ' ' node as omitted.
- //
- // If i is undefined, then the first child will be marked as such.
- //
- // If mulitple is truthy then all whitespace will be stripped out until non-whitespace
- // content is met.
- function omitRight(statements, i, multiple) {
- var current = statements[i == null ? 0 : i + 1];
- if (!current || current.type !== 'content' || (!multiple && current.rightStripped)) {
- return;
- }
-
- var original = current.string;
- current.string = current.string.replace(multiple ? (/^\s+/) : (/^[ \t]*\r?\n?/), '');
- current.rightStripped = current.string !== original;
- }
-
- // Marks the node to the left of the position as omitted.
- // I.e. ' '{{foo}} will mark the ' ' node as omitted.
- //
- // If i is undefined then the last child will be marked as such.
- //
- // If mulitple is truthy then all whitespace will be stripped out until non-whitespace
- // content is met.
- function omitLeft(statements, i, multiple) {
- var current = statements[i == null ? statements.length - 1 : i - 1];
- if (!current || current.type !== 'content' || (!multiple && current.leftStripped)) {
- return;
- }
-
- // We omit the last node if it's whitespace only and not preceeded by a non-content node.
- var original = current.string;
- current.string = current.string.replace(multiple ? (/\s+$/) : (/[ \t]+$/), '');
- current.leftStripped = current.string !== original;
- return current.leftStripped;
- }
- return __exports__;
-})(__module5__);
-
-// handlebars/compiler/base.js
-var __module8__ = (function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) {
- "use strict";
- var __exports__ = {};
- var parser = __dependency1__;
- var AST = __dependency2__;
- var Helpers = __dependency3__;
- var extend = __dependency4__.extend;
-
- __exports__.parser = parser;
-
- var yy = {};
- extend(yy, Helpers, AST);
-
- function parse(input) {
- // Just return if an already-compile AST was passed in.
- if (input.constructor === AST.ProgramNode) { return input; }
-
- parser.yy = yy;
-
- return parser.parse(input);
- }
-
- __exports__.parse = parse;
- return __exports__;
-})(__module9__, __module7__, __module10__, __module3__);
-
-// handlebars/compiler/compiler.js
-var __module11__ = (function(__dependency1__, __dependency2__) {
- "use strict";
- var __exports__ = {};
- var Exception = __dependency1__;
- var isArray = __dependency2__.isArray;
-
- var slice = [].slice;
-
- function Compiler() {}
-
- __exports__.Compiler = Compiler;// the foundHelper register will disambiguate helper lookup from finding a
- // function in a context. This is necessary for mustache compatibility, which
- // requires that context functions in blocks are evaluated by blockHelperMissing,
- // and then proceed as if the resulting value was provided to blockHelperMissing.
-
- Compiler.prototype = {
- compiler: Compiler,
-
- equals: function(other) {
- var len = this.opcodes.length;
- if (other.opcodes.length !== len) {
- return false;
- }
-
- for (var i = 0; i < len; i++) {
- var opcode = this.opcodes[i],
- otherOpcode = other.opcodes[i];
- if (opcode.opcode !== otherOpcode.opcode || !argEquals(opcode.args, otherOpcode.args)) {
- return false;
- }
- }
-
- // We know that length is the same between the two arrays because they are directly tied
- // to the opcode behavior above.
- len = this.children.length;
- for (i = 0; i < len; i++) {
- if (!this.children[i].equals(other.children[i])) {
- return false;
- }
- }
-
- return true;
- },
-
- guid: 0,
-
- compile: function(program, options) {
- this.opcodes = [];
- this.children = [];
- this.depths = {list: []};
- this.options = options;
- this.stringParams = options.stringParams;
- this.trackIds = options.trackIds;
-
- // These changes will propagate to the other compiler components
- var knownHelpers = this.options.knownHelpers;
- this.options.knownHelpers = {
- 'helperMissing': true,
- 'blockHelperMissing': true,
- 'each': true,
- 'if': true,
- 'unless': true,
- 'with': true,
- 'log': true,
- 'lookup': true
- };
- if (knownHelpers) {
- for (var name in knownHelpers) {
- this.options.knownHelpers[name] = knownHelpers[name];
- }
- }
-
- return this.accept(program);
- },
-
- accept: function(node) {
- return this[node.type](node);
- },
-
- program: function(program) {
- var statements = program.statements;
-
- for(var i=0, l=statements.length; i< 2) { continue; }
- else { this.addDepth(depth - 1); }
- }
-
- return guid;
- },
-
- block: function(block) {
- var mustache = block.mustache,
- program = block.program,
- inverse = block.inverse;
-
- if (program) {
- program = this.compileProgram(program);
- }
-
- if (inverse) {
- inverse = this.compileProgram(inverse);
- }
-
- var sexpr = mustache.sexpr;
- var type = this.classifySexpr(sexpr);
-
- if (type === "helper") {
- this.helperSexpr(sexpr, program, inverse);
- } else if (type === "simple") {
- this.simpleSexpr(sexpr);
-
- // now that the simple mustache is resolved, we need to
- // evaluate it by executing `blockHelperMissing`
- this.opcode('pushProgram', program);
- this.opcode('pushProgram', inverse);
- this.opcode('emptyHash');
- this.opcode('blockValue', sexpr.id.original);
- } else {
- this.ambiguousSexpr(sexpr, program, inverse);
-
- // now that the simple mustache is resolved, we need to
- // evaluate it by executing `blockHelperMissing`
- this.opcode('pushProgram', program);
- this.opcode('pushProgram', inverse);
- this.opcode('emptyHash');
- this.opcode('ambiguousBlockValue');
- }
-
- this.opcode('append');
- },
-
- hash: function(hash) {
- var pairs = hash.pairs, i, l;
-
- this.opcode('pushHash');
-
- for(i=0, l=pairs.length; i < a.length; i++) {
- if (!argEquals(a[i], b[i])) {
- return false;
- }
- }
- return true;
- }
- }
- return __exports__;
-})(__module5__, __module3__);
-
-// handlebars/compiler/javascript-compiler.js
-var __module12__ = (function(__dependency1__, __dependency2__) {
- "use strict";
- var __exports__;
- var COMPILER_REVISION = __dependency1__.COMPILER_REVISION;
- var REVISION_CHANGES = __dependency1__.REVISION_CHANGES;
- var Exception = __dependency2__;
-
- function Literal(value) {
- this.value = value;
- }
-
- function JavaScriptCompiler() {}
-
- JavaScriptCompiler.prototype = {
- // PUBLIC API: You can override these methods in a subclass to provide
- // alternative compiled forms for name lookup and buffering semantics
- nameLookup: function(parent, name /* , type*/) {
- if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) {
- return parent + "." + name;
- } else {
- return parent + "['" + name + "']";
- }
- },
- depthedLookup: function(name) {
- this.aliases.lookup = 'this.lookup';
-
- return 'lookup(depths, "' + name + '")';
- },
-
- compilerInfo: function() {
- var revision = COMPILER_REVISION,
- versions = REVISION_CHANGES[revision];
- return [revision, versions];
- },
-
- appendToBuffer: function(string) {
- if (this.environment.isSimple) {
- return "return " + string + ";";
- } else {
- return {
- appendToBuffer: true,
- content: string,
- toString: function() { return "buffer += " + string + ";"; }
- };
- }
- },
-
- initializeBuffer: function() {
- return this.quotedString("");
- },
-
- namespace: "Handlebars",
- // END PUBLIC API
-
- compile: function(environment, options, context, asObject) {
- this.environment = environment;
- this.options = options;
- this.stringParams = this.options.stringParams;
- this.trackIds = this.options.trackIds;
- this.precompile = !asObject;
-
- this.name = this.environment.name;
- this.isChild = !!context;
- this.context = context || {
- programs: [],
- environments: []
- };
-
- this.preamble();
-
- this.stackSlot = 0;
- this.stackVars = [];
- this.aliases = {};
- this.registers = { list: [] };
- this.hashes = [];
- this.compileStack = [];
- this.inlineStack = [];
-
- this.compileChildren(environment, options);
-
- this.useDepths = this.useDepths || environment.depths.list.length || this.options.compat;
-
- var opcodes = environment.opcodes,
- opcode,
- i,
- l;
-
- for (i = 0, l = opcodes.length; i < l; i++) {
- opcode = opcodes[i];
-
- this[opcode.opcode].apply(this, opcode.args);
- }
-
- // Flush any trailing content that might be pending.
- this.pushSource('');
-
- /* istanbul ignore next */
- if (this.stackSlot || this.inlineStack.length || this.compileStack.length) {
- throw new Exception('Compile completed with content left on stack');
- }
-
- var fn = this.createFunctionContext(asObject);
- if (!this.isChild) {
- var ret = {
- compiler: this.compilerInfo(),
- main: fn
- };
- var programs = this.context.programs;
- for (i = 0, l = programs.length; i < l; i++) {
- if (programs[i]) {
- ret[i] = programs[i];
- }
- }
-
- if (this.environment.usePartial) {
- ret.usePartial = true;
- }
- if (this.options.data) {
- ret.useData = true;
- }
- if (this.useDepths) {
- ret.useDepths = true;
- }
- if (this.options.compat) {
- ret.compat = true;
- }
-
- if (!asObject) {
- ret.compiler = JSON.stringify(ret.compiler);
- ret = this.objectLiteral(ret);
- }
-
- return ret;
- } else {
- return fn;
- }
- },
-
- preamble: function() {
- // track the last context pushed into place to allow skipping the
- // getContext opcode when it would be a noop
- this.lastContext = 0;
- this.source = [];
- },
-
- createFunctionContext: function(asObject) {
- var varDeclarations = '';
-
- var locals = this.stackVars.concat(this.registers.list);
- if(locals.length > 0) {
- varDeclarations += ", " + locals.join(", ");
- }
-
- // Generate minimizer alias mappings
- for (var alias in this.aliases) {
- if (this.aliases.hasOwnProperty(alias)) {
- varDeclarations += ', ' + alias + '=' + this.aliases[alias];
- }
- }
-
- var params = ["depth0", "helpers", "partials", "data"];
-
- if (this.useDepths) {
- params.push('depths');
- }
-
- // Perform a second pass over the output to merge content when possible
- var source = this.mergeSource(varDeclarations);
-
- if (asObject) {
- params.push(source);
-
- return Function.apply(this, params);
- } else {
- return 'function(' + params.join(',') + ') {\n ' + source + '}';
- }
- },
- mergeSource: function(varDeclarations) {
- var source = '',
- buffer,
- appendOnly = !this.forceBuffer,
- appendFirst;
-
- for (var i = 0, len = this.source.length; i < len; i++) {
- var line = this.source[i];
- if (line.appendToBuffer) {
- if (buffer) {
- buffer = buffer + '\n + ' + line.content;
- } else {
- buffer = line.content;
- }
- } else {
- if (buffer) {
- if (!source) {
- appendFirst = true;
- source = buffer + ';\n ';
- } else {
- source += 'buffer += ' + buffer + ';\n ';
- }
- buffer = undefined;
- }
- source += line + '\n ';
-
- if (!this.environment.isSimple) {
- appendOnly = false;
- }
- }
- }
-
- if (appendOnly) {
- if (buffer || !source) {
- source += 'return ' + (buffer || '""') + ';\n';
- }
- } else {
- varDeclarations += ", buffer = " + (appendFirst ? '' : this.initializeBuffer());
- if (buffer) {
- source += 'return buffer + ' + buffer + ';\n';
- } else {
- source += 'return buffer;\n';
- }
- }
-
- if (varDeclarations) {
- source = 'var ' + varDeclarations.substring(2) + (appendFirst ? '' : ';\n ') + source;
- }
-
- return source;
- },
-
- // [blockValue]
- //
- // On stack, before: hash, inverse, program, value
- // On stack, after: return value of blockHelperMissing
- //
- // The purpose of this opcode is to take a block of the form
- // `{{#this.foo}}...{{/this.foo}}`, resolve the value of `foo`, and
- // replace it on the stack with the result of properly
- // invoking blockHelperMissing.
- blockValue: function(name) {
- this.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
-
- var params = [this.contextName(0)];
- this.setupParams(name, 0, params);
-
- var blockName = this.popStack();
- params.splice(1, 0, blockName);
-
- this.push('blockHelperMissing.call(' + params.join(', ') + ')');
- },
-
- // [ambiguousBlockValue]
- //
- // On stack, before: hash, inverse, program, value
- // Compiler value, before: lastHelper=value of last found helper, if any
- // On stack, after, if no lastHelper: same as [blockValue]
- // On stack, after, if lastHelper: value
- ambiguousBlockValue: function() {
- this.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
-
- // We're being a bit cheeky and reusing the options value from the prior exec
- var params = [this.contextName(0)];
- this.setupParams('', 0, params, true);
-
- this.flushInline();
-
- var current = this.topStack();
- params.splice(1, 0, current);
-
- this.pushSource("if (!" + this.lastHelper + ") { " + current + " = blockHelperMissing.call(" + params.join(", ") + "); }");
- },
-
- // [appendContent]
- //
- // On stack, before: ...
- // On stack, after: ...
- //
- // Appends the string value of `content` to the current buffer
- appendContent: function(content) {
- if (this.pendingContent) {
- content = this.pendingContent + content;
- }
-
- this.pendingContent = content;
- },
-
- // [append]
- //
- // On stack, before: value, ...
- // On stack, after: ...
- //
- // Coerces `value` to a String and appends it to the current buffer.
- //
- // If `value` is truthy, or 0, it is coerced into a string and appended
- // Otherwise, the empty string is appended
- append: function() {
- // Force anything that is inlined onto the stack so we don't have duplication
- // when we examine local
- this.flushInline();
- var local = this.popStack();
- this.pushSource('if (' + local + ' != null) { ' + this.appendToBuffer(local) + ' }');
- if (this.environment.isSimple) {
- this.pushSource("else { " + this.appendToBuffer("''") + " }");
- }
- },
-
- // [appendEscaped]
- //
- // On stack, before: value, ...
- // On stack, after: ...
- //
- // Escape `value` and append it to the buffer
- appendEscaped: function() {
- this.aliases.escapeExpression = 'this.escapeExpression';
-
- this.pushSource(this.appendToBuffer("escapeExpression(" + this.popStack() + ")"));
- },
-
- // [getContext]
- //
- // On stack, before: ...
- // On stack, after: ...
- // Compiler value, after: lastContext=depth
- //
- // Set the value of the `lastContext` compiler value to the depth
- getContext: function(depth) {
- this.lastContext = depth;
- },
-
- // [pushContext]
- //
- // On stack, before: ...
- // On stack, after: currentContext, ...
- //
- // Pushes the value of the current context onto the stack.
- pushContext: function() {
- this.pushStackLiteral(this.contextName(this.lastContext));
- },
-
- // [lookupOnContext]
- //
- // On stack, before: ...
- // On stack, after: currentContext[name], ...
- //
- // Looks up the value of `name` on the current context and pushes
- // it onto the stack.
- lookupOnContext: function(parts, falsy, scoped) {
- /*jshint -W083 */
- var i = 0,
- len = parts.length;
-
- if (!scoped && this.options.compat && !this.lastContext) {
- // The depthed query is expected to handle the undefined logic for the root level that
- // is implemented below, so we evaluate that directly in compat mode
- this.push(this.depthedLookup(parts[i++]));
- } else {
- this.pushContext();
- }
-
- for (; i < len; i++) {
- this.replaceStack(function(current) {
- var lookup = this.nameLookup(current, parts[i], 'context');
- // We want to ensure that zero and false are handled properly if the context (falsy flag)
- // needs to have the special handling for these values.
- if (!falsy) {
- return ' != null ? ' + lookup + ' : ' + current;
- } else {
- // Otherwise we can use generic falsy handling
- return ' && ' + lookup;
- }
- });
- }
- },
-
- // [lookupData]
- //
- // On stack, before: ...
- // On stack, after: data, ...
- //
- // Push the data lookup operator
- lookupData: function(depth, parts) {
- /*jshint -W083 */
- if (!depth) {
- this.pushStackLiteral('data');
- } else {
- this.pushStackLiteral('this.data(data, ' + depth + ')');
- }
-
- var len = parts.length;
- for (var i = 0; i < len; i++) {
- this.replaceStack(function(current) {
- return ' && ' + this.nameLookup(current, parts[i], 'data');
- });
- }
- },
-
- // [resolvePossibleLambda]
- //
- // On stack, before: value, ...
- // On stack, after: resolved value, ...
- //
- // If the `value` is a lambda, replace it on the stack by
- // the return value of the lambda
- resolvePossibleLambda: function() {
- this.aliases.lambda = 'this.lambda';
-
- this.push('lambda(' + this.popStack() + ', ' + this.contextName(0) + ')');
- },
-
- // [pushStringParam]
- //
- // On stack, before: ...
- // On stack, after: string, currentContext, ...
- //
- // This opcode is designed for use in string mode, which
- // provides the string value of a parameter along with its
- // depth rather than resolving it immediately.
- pushStringParam: function(string, type) {
- this.pushContext();
- this.pushString(type);
-
- // If it's a subexpression, the string result
- // will be pushed after this opcode.
- if (type !== 'sexpr') {
- if (typeof string === 'string') {
- this.pushString(string);
- } else {
- this.pushStackLiteral(string);
- }
- }
- },
-
- emptyHash: function() {
- this.pushStackLiteral('{}');
-
- if (this.trackIds) {
- this.push('{}'); // hashIds
- }
- if (this.stringParams) {
- this.push('{}'); // hashContexts
- this.push('{}'); // hashTypes
- }
- },
- pushHash: function() {
- if (this.hash) {
- this.hashes.push(this.hash);
- }
- this.hash = {values: [], types: [], contexts: [], ids: []};
- },
- popHash: function() {
- var hash = this.hash;
- this.hash = this.hashes.pop();
-
- if (this.trackIds) {
- this.push('{' + hash.ids.join(',') + '}');
- }
- if (this.stringParams) {
- this.push('{' + hash.contexts.join(',') + '}');
- this.push('{' + hash.types.join(',') + '}');
- }
-
- this.push('{\n ' + hash.values.join(',\n ') + '\n }');
- },
-
- // [pushString]
- //
- // On stack, before: ...
- // On stack, after: quotedString(string), ...
- //
- // Push a quoted version of `string` onto the stack
- pushString: function(string) {
- this.pushStackLiteral(this.quotedString(string));
- },
-
- // [push]
- //
- // On stack, before: ...
- // On stack, after: expr, ...
- //
- // Push an expression onto the stack
- push: function(expr) {
- this.inlineStack.push(expr);
- return expr;
- },
-
- // [pushLiteral]
- //
- // On stack, before: ...
- // On stack, after: value, ...
- //
- // Pushes a value onto the stack. This operation prevents
- // the compiler from creating a temporary variable to hold
- // it.
- pushLiteral: function(value) {
- this.pushStackLiteral(value);
- },
-
- // [pushProgram]
- //
- // On stack, before: ...
- // On stack, after: program(guid), ...
- //
- // Push a program expression onto the stack. This takes
- // a compile-time guid and converts it into a runtime-accessible
- // expression.
- pushProgram: function(guid) {
- if (guid != null) {
- this.pushStackLiteral(this.programExpression(guid));
- } else {
- this.pushStackLiteral(null);
- }
- },
-
- // [invokeHelper]
- //
- // On stack, before: hash, inverse, program, params..., ...
- // On stack, after: result of helper invocation
- //
- // Pops off the helper's parameters, invokes the helper,
- // and pushes the helper's return value onto the stack.
- //
- // If the helper is not found, `helperMissing` is called.
- invokeHelper: function(paramSize, name, isSimple) {
- this.aliases.helperMissing = 'helpers.helperMissing';
-
- var nonHelper = this.popStack();
- var helper = this.setupHelper(paramSize, name);
-
- var lookup = (isSimple ? helper.name + ' || ' : '') + nonHelper + ' || helperMissing';
- this.push('((' + lookup + ').call(' + helper.callParams + '))');
- },
-
- // [invokeKnownHelper]
- //
- // On stack, before: hash, inverse, program, params..., ...
- // On stack, after: result of helper invocation
- //
- // This operation is used when the helper is known to exist,
- // so a `helperMissing` fallback is not required.
- invokeKnownHelper: function(paramSize, name) {
- var helper = this.setupHelper(paramSize, name);
- this.push(helper.name + ".call(" + helper.callParams + ")");
- },
-
- // [invokeAmbiguous]
- //
- // On stack, before: hash, inverse, program, params..., ...
- // On stack, after: result of disambiguation
- //
- // This operation is used when an expression like `{{foo}}`
- // is provided, but we don't know at compile-time whether it
- // is a helper or a path.
- //
- // This operation emits more code than the other options,
- // and can be avoided by passing the `knownHelpers` and
- // `knownHelpersOnly` flags at compile-time.
- invokeAmbiguous: function(name, helperCall) {
- this.aliases.functionType = '"function"';
- this.aliases.helperMissing = 'helpers.helperMissing';
- this.useRegister('helper');
-
- var nonHelper = this.popStack();
-
- this.emptyHash();
- var helper = this.setupHelper(0, name, helperCall);
-
- var helperName = this.lastHelper = this.nameLookup('helpers', name, 'helper');
-
- this.push(
- '((helper = (helper = ' + helperName + ' || ' + nonHelper + ') != null ? helper : helperMissing'
- + (helper.paramsInit ? '),(' + helper.paramsInit : '') + '),'
- + '(typeof helper === functionType ? helper.call(' + helper.callParams + ') : helper))');
- },
-
- // [invokePartial]
- //
- // On stack, before: context, ...
- // On stack after: result of partial invocation
- //
- // This operation pops off a context, invokes a partial with that context,
- // and pushes the result of the invocation back.
- invokePartial: function(name, indent) {
- var params = [this.nameLookup('partials', name, 'partial'), "'" + indent + "'", "'" + name + "'", this.popStack(), this.popStack(), "helpers", "partials"];
-
- if (this.options.data) {
- params.push("data");
- } else if (this.options.compat) {
- params.push('undefined');
- }
- if (this.options.compat) {
- params.push('depths');
- }
-
- this.push("this.invokePartial(" + params.join(", ") + ")");
- },
-
- // [assignToHash]
- //
- // On stack, before: value, ..., hash, ...
- // On stack, after: ..., hash, ...
- //
- // Pops a value off the stack and assigns it to the current hash
- assignToHash: function(key) {
- var value = this.popStack(),
- context,
- type,
- id;
-
- if (this.trackIds) {
- id = this.popStack();
- }
- if (this.stringParams) {
- type = this.popStack();
- context = this.popStack();
- }
-
- var hash = this.hash;
- if (context) {
- hash.contexts.push("'" + key + "': " + context);
- }
- if (type) {
- hash.types.push("'" + key + "': " + type);
- }
- if (id) {
- hash.ids.push("'" + key + "': " + id);
- }
- hash.values.push("'" + key + "': (" + value + ")");
- },
-
- pushId: function(type, name) {
- if (type === 'ID' || type === 'DATA') {
- this.pushString(name);
- } else if (type === 'sexpr') {
- this.pushStackLiteral('true');
- } else {
- this.pushStackLiteral('null');
- }
- },
-
- // HELPERS
-
- compiler: JavaScriptCompiler,
-
- compileChildren: function(environment, options) {
- var children = environment.children, child, compiler;
-
- for(var i=0, l=children.length; i < len; i++) {
- var environment = this.context.environments[i];
- if (environment && environment.equals(child)) {
- return i;
- }
- }
- },
-
- programExpression: function(guid) {
- var child = this.environment.children[guid],
- depths = child.depths.list,
- useDepths = this.useDepths,
- depth;
-
- var programParams = [child.index, 'data'];
-
- if (useDepths) {
- programParams.push('depths');
- }
-
- return 'this.program(' + programParams.join(', ') + ')';
- },
-
- useRegister: function(name) {
- if(!this.registers[name]) {
- this.registers[name] = true;
- this.registers.list.push(name);
- }
- },
-
- pushStackLiteral: function(item) {
- return this.push(new Literal(item));
- },
-
- pushSource: function(source) {
- if (this.pendingContent) {
- this.source.push(this.appendToBuffer(this.quotedString(this.pendingContent)));
- this.pendingContent = undefined;
- }
-
- if (source) {
- this.source.push(source);
- }
- },
-
- pushStack: function(item) {
- this.flushInline();
-
- var stack = this.incrStack();
- this.pushSource(stack + " = " + item + ";");
- this.compileStack.push(stack);
- return stack;
- },
-
- replaceStack: function(callback) {
- var prefix = '',
- inline = this.isInline(),
- stack,
- createdStack,
- usedLiteral;
-
- /* istanbul ignore next */
- if (!this.isInline()) {
- throw new Exception('replaceStack on non-inline');
- }
-
- // We want to merge the inline statement into the replacement statement via ','
- var top = this.popStack(true);
-
- if (top instanceof Literal) {
- // Literals do not need to be inlined
- prefix = stack = top.value;
- usedLiteral = true;
- } else {
- // Get or create the current stack name for use by the inline
- createdStack = !this.stackSlot;
- var name = !createdStack ? this.topStackName() : this.incrStack();
-
- prefix = '(' + this.push(name) + ' = ' + top + ')';
- stack = this.topStack();
- }
-
- var item = callback.call(this, stack);
-
- if (!usedLiteral) {
- this.popStack();
- }
- if (createdStack) {
- this.stackSlot--;
- }
- this.push('(' + prefix + item + ')');
- },
-
- incrStack: function() {
- this.stackSlot++;
- if(this.stackSlot > this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); }
- return this.topStackName();
- },
- topStackName: function() {
- return "stack" + this.stackSlot;
- },
- flushInline: function() {
- var inlineStack = this.inlineStack;
- if (inlineStack.length) {
- this.inlineStack = [];
- for (var i = 0, len = inlineStack.length; i < len; i++) {
- var entry = inlineStack[i];
- if (entry instanceof Literal) {
- this.compileStack.push(entry);
- } else {
- this.pushStack(entry);
- }
- }
- }
- },
- isInline: function() {
- return this.inlineStack.length;
- },
-
- popStack: function(wrapped) {
- var inline = this.isInline(),
- item = (inline ? this.inlineStack : this.compileStack).pop();
-
- if (!wrapped && (item instanceof Literal)) {
- return item.value;
- } else {
- if (!inline) {
- /* istanbul ignore next */
- if (!this.stackSlot) {
- throw new Exception('Invalid stack pop');
- }
- this.stackSlot--;
- }
- return item;
- }
- },
-
- topStack: function() {
- var stack = (this.isInline() ? this.inlineStack : this.compileStack),
- item = stack[stack.length - 1];
-
- if (item instanceof Literal) {
- return item.value;
- } else {
- return item;
- }
- },
-
- contextName: function(context) {
- if (this.useDepths && context) {
- return 'depths[' + context + ']';
- } else {
- return 'depth' + context;
- }
- },
-
- quotedString: function(str) {
- return '"' + str
- .replace(/\\/g, '\\\\')
- .replace(/"/g, '\\"')
- .replace(/\n/g, '\\n')
- .replace(/\r/g, '\\r')
- .replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4
- .replace(/\u2029/g, '\\u2029') + '"';
- },
-
- objectLiteral: function(obj) {
- var pairs = [];
-
- for (var key in obj) {
- if (obj.hasOwnProperty(key)) {
- pairs.push(this.quotedString(key) + ':' + obj[key]);
- }
- }
-
- return '{' + pairs.join(',') + '}';
- },
-
- setupHelper: function(paramSize, name, blockHelper) {
- var params = [],
- paramsInit = this.setupParams(name, paramSize, params, blockHelper);
- var foundHelper = this.nameLookup('helpers', name, 'helper');
-
- return {
- params: params,
- paramsInit: paramsInit,
- name: foundHelper,
- callParams: [this.contextName(0)].concat(params).join(", ")
- };
- },
-
- setupOptions: function(helper, paramSize, params) {
- var options = {}, contexts = [], types = [], ids = [], param, inverse, program;
-
- options.name = this.quotedString(helper);
- options.hash = this.popStack();
-
- if (this.trackIds) {
- options.hashIds = this.popStack();
- }
- if (this.stringParams) {
- options.hashTypes = this.popStack();
- options.hashContexts = this.popStack();
- }
-
- inverse = this.popStack();
- program = this.popStack();
-
- // Avoid setting fn and inverse if neither are set. This allows
- // helpers to do a check for `if (options.fn)`
- if (program || inverse) {
- if (!program) {
- program = 'this.noop';
- }
-
- if (!inverse) {
- inverse = 'this.noop';
- }
-
- options.fn = program;
- options.inverse = inverse;
- }
-
- // The parameters go on to the stack in order (making sure that they are evaluated in order)
- // so we need to pop them off the stack in reverse order
- var i = paramSize;
- while (i--) {
- param = this.popStack();
- params[i] = param;
-
- if (this.trackIds) {
- ids[i] = this.popStack();
- }
- if (this.stringParams) {
- types[i] = this.popStack();
- contexts[i] = this.popStack();
- }
- }
-
- if (this.trackIds) {
- options.ids = "[" + ids.join(",") + "]";
- }
- if (this.stringParams) {
- options.types = "[" + types.join(",") + "]";
- options.contexts = "[" + contexts.join(",") + "]";
- }
-
- if (this.options.data) {
- options.data = "data";
- }
-
- return options;
- },
-
- // the params and contexts arguments are passed in arrays
- // to fill in
- setupParams: function(helperName, paramSize, params, useRegister) {
- var options = this.objectLiteral(this.setupOptions(helperName, paramSize, params));
-
- if (useRegister) {
- this.useRegister('options');
- params.push('options');
- return 'options=' + options;
- } else {
- params.push(options);
- return '';
- }
- }
- };
-
- var reservedWords = (
- "break else new var" +
- " case finally return void" +
- " catch for switch while" +
- " continue function this with" +
- " default if throw" +
- " delete in try" +
- " do instanceof typeof" +
- " abstract enum int short" +
- " boolean export interface static" +
- " byte extends long super" +
- " char final native synchronized" +
- " class float package throws" +
- " const goto private transient" +
- " debugger implements protected volatile" +
- " double import public let yield"
- ).split(" ");
-
- var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {};
-
- for(var i=0, l=reservedWords.length; i <0&&(s[r]=t[r]);if(null!=t&&"function"==typeof Object.getOwnPropertySymbols){var o=0;for(r=Object.getOwnPropertySymbols(t);o<0&&Object.prototype.propertyIsEnumerable.call(t,r[o])&&(s[r[o]]=t[r[o]])}return s}class s{static isAdvancedMarkerAvailable(t){return google.maps.marker&&!0===t.getMapCapabilities().isAdvancedMarkersAvailable}static isAdvancedMarker(t){return google.maps.marker&&t instanceof google.maps.marker.AdvancedMarkerElement}static setMap(t,e){this.isAdvancedMarker(t)?t.map=e:t.setMap(e)}static getPosition(t){if(this.isAdvancedMarker(t)){if(t.position){if(t.position instanceof google.maps.LatLng)return t.position;if(t.position.lat&&t.position.lng)return new google.maps.LatLng(t.position.lat,t.position.lng)}return new google.maps.LatLng(null)}return t.getPosition()}static getVisible(t){return!!this.isAdvancedMarker(t)||t.getVisible()}}class r{constructor(t){let{markers:e,position:s}=t;this.markers=e,s&&(s instanceof google.maps.LatLng?this._position=s:this._position=new google.maps.LatLng(s))}get bounds(){if(0===this.markers.length&&!this._position)return;const t=new google.maps.LatLngBounds(this._position,this._position);for(const e of this.markers)t.extend(s.getPosition(e));return t}get position(){return this._position||this.bounds.getCenter()}get count(){return this.markers.filter((t=>s.getVisible(t))).length}push(t){this.markers.push(t)}delete(){this.marker&&(s.setMap(this.marker,null),this.marker=void 0),this.markers.length=0}}const o=(t,e,r,o)=>{const n=i(t.getBounds(),e,o);return r.filter((t=>n.contains(s.getPosition(t))))},i=(t,e,s)=>{const{northEast:r,southWest:o}=h(t,e),i=l({northEast:r,southWest:o},s);return c(i,e)},n=(t,e,s)=>{const r=i(t,e,s),o=r.getNorthEast(),n=r.getSouthWest();return[n.lng(),n.lat(),o.lng(),o.lat()]},a=(t,e)=>{const s=(e.lat-t.lat)*Math.PI/180,r=(e.lng-t.lng)*Math.PI/180,o=Math.sin(s/2),i=Math.sin(r/2),n=o*o+Math.cos(t.lat*Math.PI/180)*Math.cos(e.lat*Math.PI/180)*i*i;return 6371*(2*Math.atan2(Math.sqrt(n),Math.sqrt(1-n)))},h=(t,e)=>({northEast:e.fromLatLngToDivPixel(t.getNorthEast()),southWest:e.fromLatLngToDivPixel(t.getSouthWest())}),l=(t,e)=>{let{northEast:s,southWest:r}=t;return s.x+=e,s.y-=e,r.x-=e,r.y+=e,{northEast:s,southWest:r}},c=(t,e)=>{let{northEast:s,southWest:r}=t;const o=e.fromDivPixelToLatLng(r),i=e.fromDivPixelToLatLng(s);return new google.maps.LatLngBounds(o,i)};class u{constructor(t){let{maxZoom:e=16}=t;this.maxZoom=e}noop(t){let{markers:e}=t;return m(e)}}class p extends u{constructor(t){var{viewportPadding:s=60}=t;super(e(t,["viewportPadding"])),this.viewportPadding=60,this.viewportPadding=s}calculate(t){let{markers:e,map:s,mapCanvasProjection:r}=t;return s.getZoom()>=this.maxZoom?{clusters:this.noop({markers:e}),changed:!1}:{clusters:this.cluster({markers:o(s,r,e,this.viewportPadding),map:s,mapCanvasProjection:r})}}}const m=t=>t.map((t=>new r({position:s.getPosition(t),markers:[t]})));function d(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var g=function t(e,s){if(e===s)return!0;if(e&&s&&"object"==typeof e&&"object"==typeof s){if(e.constructor!==s.constructor)return!1;var r,o,i;if(Array.isArray(e)){if((r=e.length)!=s.length)return!1;for(o=r;0!=o--;)if(!t(e[o],s[o]))return!1;return!0}if(e.constructor===RegExp)return e.source===s.source&&e.flags===s.flags;if(e.valueOf!==Object.prototype.valueOf)return e.valueOf()===s.valueOf();if(e.toString!==Object.prototype.toString)return e.toString()===s.toString();if((r=(i=Object.keys(e)).length)!==Object.keys(s).length)return!1;for(o=r;0!=o--;)if(!Object.prototype.hasOwnProperty.call(s,i[o]))return!1;for(o=r;0!=o--;){var n=i[o];if(!t(e[n],s[n]))return!1}return!0}return e!=e&&s!=s},f=d(g);const k=[Int8Array,Uint8Array,Uint8ClampedArray,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array];class w{static from(t){if(!(t instanceof ArrayBuffer))throw new Error("Data must be an instance of ArrayBuffer.");const[e,s]=new Uint8Array(t,0,2);if(219!==e)throw new Error("Data does not appear to be in a KDBush format.");const r=s>>4;if(1!==r)throw new Error(`Got v${r} data when expected v1.`);const o=k[15&s];if(!o)throw new Error("Unrecognized array type.");const[i]=new Uint16Array(t,2,1),[n]=new Uint32Array(t,4,1);return new w(n,i,o,t)}constructor(t,e=64,s=Float64Array,r){if(isNaN(t)||t<0)throw new Error(`Unpexpected numItems value: ${t}.`);this.numItems=+t,this.nodeSize=Math.min(Math.max(+e,2),65535),this.ArrayType=s,this.IndexArrayType=t<65536?Uint16Array:Uint32Array;const o=k.indexOf(this.ArrayType),i=2*t*this.ArrayType.BYTES_PER_ELEMENT,n=t*this.IndexArrayType.BYTES_PER_ELEMENT,a=(8-n%8)%8;if(o<0)throw new Error(`Unexpected typed array class: ${s}.`);r&&r instanceof ArrayBuffer?(this.data=r,this.ids=new this.IndexArrayType(this.data,8,t),this.coords=new this.ArrayType(this.data,8+n+a,2*t),this._pos=2*t,this._finished=!0):(this.data=new ArrayBuffer(8+i+n+a),this.ids=new this.IndexArrayType(this.data,8,t),this.coords=new this.ArrayType(this.data,8+n+a,2*t),this._pos=0,this._finished=!1,new Uint8Array(this.data,0,2).set([219,16+o]),new Uint16Array(this.data,2,1)[0]=e,new Uint32Array(this.data,4,1)[0]=t)}add(t,e){const s=this._pos>>1;return this.ids[s]=s,this.coords[this._pos++]=t,this.coords[this._pos++]=e,s}finish(){const t=this._pos>>1;if(t!==this.numItems)throw new Error(`Added ${t} items when expected ${this.numItems}.`);return y(this.ids,this.coords,this.nodeSize,0,this.numItems-1,0),this._finished=!0,this}range(t,e,s,r){if(!this._finished)throw new Error("Data not yet indexed - call index.finish().");const{ids:o,coords:i,nodeSize:n}=this,a=[0,o.length-1,0],h=[];for(;a.length;){const l=a.pop()||0,c=a.pop()||0,u=a.pop()||0;if(c-u<=n){for(let n=u;n<=c;n++){const a=i[2*n],l=i[2*n+1];a>=t&&a<=s&&l>=e&&l<=r&&h.push(o[n])}continue}const p=u+c>>1,m=i[2*p],d=i[2*p+1];m>=t&&m<=s&&d>=e&&d<=r&&h.push(o[p]),(0===l?t<=m:e<=d)&&(a.push(u),a.push(p-1),a.push(1-l)),(0===l?s>=m:r>=d)&&(a.push(p+1),a.push(c),a.push(1-l))}return h}within(t,e,s){if(!this._finished)throw new Error("Data not yet indexed - call index.finish().");const{ids:r,coords:o,nodeSize:i}=this,n=[0,r.length-1,0],a=[],h=s*s;for(;n.length;){const l=n.pop()||0,c=n.pop()||0,u=n.pop()||0;if(c-u<=i){for(let s=u;s<=c;s++)C(o[2*s],o[2*s+1],t,e)<=h&&a.push(r[s]);continue}const p=u+c>>1,m=o[2*p],d=o[2*p+1];C(m,d,t,e)<=h&&a.push(r[p]),(0===l?t-s<=m:e-s<=d)&&(n.push(u),n.push(p-1),n.push(1-l)),(0===l?t+s>=m:e+s>=d)&&(n.push(p+1),n.push(c),n.push(1-l))}return a}}function y(t,e,s,r,o,i){if(o-r<=s)return;const n=r+o>>1;M(t,e,n,r,o,i),y(t,e,s,r,n-1,1-i),y(t,e,s,n+1,o,1-i)}function M(t,e,s,r,o,i){for(;o>r;){if(o-r>600){const n=o-r+1,a=s-r+1,h=Math.log(n),l=.5*Math.exp(2*h/3),c=.5*Math.sqrt(h*l*(n-l)/n)*(a-n/2<0?-1:1);M(t,e,s,Math.max(r,Math.floor(s-a*l/n+c)),Math.min(o,Math.floor(s+(n-a)*l/n+c)),i)}const n=e[2*s+i];let a=r,h=o;for(v(t,e,r,s),e[2*o+i]>n&&v(t,e,r,o);an;)h--}e[2*r+i]===n?v(t,e,r,h):(h++,v(t,e,h,o)),h<=s&&(r=h+1),s<=h&&(o=h-1)}}function v(t,e,s,r){x(t,s,r),x(e,2*s,2*r),x(e,2*s+1,2*r+1)}function x(t,e,s){const r=t[e];t[e]=t[s],t[s]=r}function C(t,e,s,r){const o=t-s,i=e-r;return o*o+i*i}const P={minZoom:0,maxZoom:16,minPoints:2,radius:40,extent:512,nodeSize:64,log:!1,generateId:!1,reduce:null,map:t=>t},_=Math.fround||(E=new Float32Array(1),t=>(E[0]=+t,E[0]));var E;const A=3,b=5,L=6;class O{constructor(t){this.options=Object.assign(Object.create(P),t),this.trees=new Array(this.options.maxZoom+1),this.stride=this.options.reduce?7:6,this.clusterProps=[]}load(t){const{log:e,minZoom:s,maxZoom:r}=this.options;e&&console.time("total time");const o=`prepare ${t.length} points`;e&&console.time(o),this.points=t;const i=[];for(let e=0;e=s;t--){const s=+Date.now();n=this.trees[t]=this._createTree(this._cluster(n,t)),e&&console.log("z%d: %d clusters in %dms",t,n.numItems,+Date.now()-s)}return e&&console.timeEnd("total time"),this}getClusters(t,e){let s=((t[0]+180)%360+360)%360-180;const r=Math.max(-90,Math.min(90,t[1]));let o=180===t[2]?180:((t[2]+180)%360+360)%360-180;const i=Math.max(-90,Math.min(90,t[3]));if(t[2]-t[0]>=360)s=-180,o=180;else if(s>o){const t=this.getClusters([s,r,180,i],e),n=this.getClusters([-180,r,o,i],e);return t.concat(n)}const n=this.trees[this._limitZoom(e)],a=n.range(T(s),j(i),T(o),j(r)),h=n.data,l=[];for(const t of a){const e=this.stride*t;l.push(h[e+b]>1?Z(h,e,this.clusterProps):this.points[h[e+A]])}return l}getChildren(t){const e=this._getOriginId(t),s=this._getOriginZoom(t),r="No cluster with the specified id.",o=this.trees[s];if(!o)throw new Error(r);const i=o.data;if(e*this.stride>=i.length)throw new Error(r);const n=this.options.radius/(this.options.extent*Math.pow(2,s-1)),a=i[e*this.stride],h=i[e*this.stride+1],l=o.within(a,h,n),c=[];for(const e of l){const s=e*this.stride;i[s+4]===t&&c.push(i[s+b]>1?Z(i,s,this.clusterProps):this.points[i[s+A]])}if(0===c.length)throw new Error(r);return c}getLeaves(t,e,s){e=e||10,s=s||0;const r=[];return this._appendLeaves(r,t,e,s,0),r}getTile(t,e,s){const r=this.trees[this._limitZoom(t)],o=Math.pow(2,t),{extent:i,radius:n}=this.options,a=n/i,h=(s-a)/o,l=(s+1+a)/o,c={features:[]};return this._addTileFeatures(r.range((e-a)/o,h,(e+1+a)/o,l),r.data,e,s,o,c),0===e&&this._addTileFeatures(r.range(1-a/o,h,1,l),r.data,o,s,o,c),e===o-1&&this._addTileFeatures(r.range(0,h,a/o,l),r.data,-1,s,o,c),c.features.length?c:null}getClusterExpansionZoom(t){let e=this._getOriginZoom(t)-1;for(;e<=this.options.maxZoom;){const s=this.getChildren(t);if(e++,1!==s.length)break;t=s[0].properties.cluster_id}return e}_appendLeaves(t,e,s,r,o){const i=this.getChildren(e);for(const e of i){const i=e.properties;if(i&&i.cluster?o+i.point_count<=r?o+=i.point_count:o=this._appendLeaves(t,i.cluster_id,s,r,o):o1;let h,l,c;if(a)h=I(e,t,this.clusterProps),l=e[t],c=e[t+1];else{const s=this.points[e[t+A]];h=s.properties;const[r,o]=s.geometry.coordinates;l=T(r),c=j(o)}const u={type:1,geometry:[[Math.round(this.options.extent*(l*o-s)),Math.round(this.options.extent*(c*o-r))]],tags:h};let p;p=a||this.options.generateId?e[t+A]:this.points[e[t+A]].id,void 0!==p&&(u.id=p),i.features.push(u)}}_limitZoom(t){return Math.max(this.options.minZoom,Math.min(Math.floor(+t),this.options.maxZoom+1))}_cluster(t,e){const{radius:s,extent:r,reduce:o,minPoints:i}=this.options,n=s/(r*Math.pow(2,e)),a=t.data,h=[],l=this.stride;for(let s=0;s<=e)continue;a[s+2]=e;const r=a[s],c=a[s+1],u=t.within(a[s],a[s+1],n),p=a[s+b];let m=p;for(const t of u){const s=t*l;a[s+2]>e&&(m+=a[s+b])}if(m>p&&m>=i){let t,i=r*p,n=c*p,d=-1;const g=((s/l|0)<<5)+(e+1)+this.points.length;for(const r of u){const h=r*l;if(a[h+2]<=e)continue;a[h+2]=e;const c=a[h+b];i+=a[h]*c,n+=a[h+1]*c,a[h+4]=g,o&&(t||(t=this._map(a,s,!0),d=this.clusterProps.length,this.clusterProps.push(t)),o(t,this._map(a,h)))}a[s+4]=g,h.push(i/m,n/m,1/0,g,-1,m),o&&h.push(d)}else{for(let t=0;t1)for(const t of u){const s=t*l;if(!(a[s+2]<=e)){a[s+2]=e;for(let t=0;t>5}_getOriginZoom(t){return(t-this.points.length)%32}_map(t,e,s){if(t[e+b]>1){const r=this.clusterProps[t[e+L]];return s?Object.assign({},r):r}const r=this.points[t[e+A]].properties,o=this.options.map(r);return s&&o===r?Object.assign({},o):o}}function Z(t,e,s){return{type:"Feature",id:t[e+A],properties:I(t,e,s),geometry:{type:"Point",coordinates:[(r=t[e],360*(r-.5)),S(t[e+1])]}};var r}function I(t,e,s){const r=t[e+b],o=r>=1e4?`${Math.round(r/1e3)}k`:r>=1e3?Math.round(r/100)/10+"k":r,i=t[e+L],n=-1===i?{}:Object.assign({},s[i]);return Object.assign(n,{cluster:!0,cluster_id:t[e+A],point_count:r,point_count_abbreviated:o})}function T(t){return t/360+.5}function j(t){const e=Math.sin(t*Math.PI/180),s=.5-.25*Math.log((1+e)/(1-e))/Math.PI;return s<0?0:s>1?1:s}function S(t){const e=(180-360*t)*Math.PI/180;return 360*Math.atan(Math.exp(e))/Math.PI-90}class z extends u{constructor(t){var{maxZoom:s,radius:r=60}=t,o=e(t,["maxZoom","radius"]);super({maxZoom:s}),this.state={zoom:-1},this.superCluster=new O(Object.assign({maxZoom:this.maxZoom,radius:r},o))}calculate(t){let e=!1;const r={zoom:t.map.getZoom()};if(!f(t.markers,this.markers)){e=!0,this.markers=[...t.markers];const r=this.markers.map((t=>{const e=s.getPosition(t);return{type:"Feature",geometry:{type:"Point",coordinates:[e.lng(),e.lat()]},properties:{marker:t}}}));this.superCluster.load(r)}return e||(this.state.zoom<=this.maxZoom||r.zoom<=this.maxZoom)&&(e=!f(this.state,r)),this.state=r,e&&(this.clusters=this.cluster(t)),{clusters:this.clusters,changed:e}}cluster(t){let{map:e}=t;return this.superCluster.getClusters([-180,-90,180,90],Math.round(e.getZoom())).map((t=>this.transformCluster(t)))}transformCluster(t){let{geometry:{coordinates:[e,o]},properties:i}=t;if(i.cluster)return new r({markers:this.superCluster.getLeaves(i.cluster_id,1/0).map((t=>t.properties.marker)),position:{lat:o,lng:e}});const n=i.marker;return new r({markers:[n],position:s.getPosition(n)})}}class U{constructor(t,e){this.markers={sum:t.length};const s=e.map((t=>t.count)),r=s.reduce(((t,e)=>t+e),0);this.clusters={count:e.length,markers:{mean:r/e.length,sum:r,min:Math.min(...s),max:Math.max(...s)}}}}class B{render(t,e,r){let{count:o,position:i}=t;const n=`\n \n \n \n${o} \n `,a=`Cluster of ${o} markers`,h=Number(google.maps.Marker.MAX_ZINDEX)+o;if(s.isAdvancedMarkerAvailable(r)){const t=(new DOMParser).parseFromString(n,"image/svg+xml").documentElement;t.setAttribute("transform","translate(0 25)");const e={map:r,position:i,zIndex:h,title:a,content:t};return new google.maps.marker.AdvancedMarkerElement(e)}const l={position:i,zIndex:h,title:a,icon:{url:`data:image/svg+xml;base64,${btoa(n)}`,anchor:new google.maps.Point(25,25)}};return new google.maps.Marker(l)}}class D{constructor(){!function(t,e){for(let s in e.prototype)t.prototype[s]=e.prototype[s]}(D,google.maps.OverlayView)}}var N;t.MarkerClustererEvents=void 0,(N=t.MarkerClustererEvents||(t.MarkerClustererEvents={})).CLUSTERING_BEGIN="clusteringbegin",N.CLUSTERING_END="clusteringend",N.CLUSTER_CLICK="click";const F=(t,e,s)=>{s.fitBounds(e.bounds)};return t.AbstractAlgorithm=u,t.AbstractViewportAlgorithm=p,t.Cluster=r,t.ClusterStats=U,t.DefaultRenderer=B,t.GridAlgorithm=class extends p{constructor(t){var{maxDistance:s=4e4,gridSize:r=40}=t;super(e(t,["maxDistance","gridSize"])),this.clusters=[],this.state={zoom:-1},this.maxDistance=s,this.gridSize=r}calculate(t){let{markers:e,map:s,mapCanvasProjection:r}=t;const i={zoom:s.getZoom()};let n=!1;return this.state.zoom>=this.maxZoom&&i.zoom>=this.maxZoom||(n=!f(this.state,i)),this.state=i,s.getZoom()>=this.maxZoom?{clusters:this.noop({markers:e}),changed:n}:{clusters:this.cluster({markers:o(s,r,e,this.viewportPadding),map:s,mapCanvasProjection:r})}}cluster(t){let{markers:e,map:s,mapCanvasProjection:r}=t;return this.clusters=[],e.forEach((t=>{this.addToClosestCluster(t,s,r)})),this.clusters}addToClosestCluster(t,e,o){let n=this.maxDistance,h=null;for(let e=0;e{this.addMarker(t,!0)})),e||this.render()}removeMarker(t,e){const r=this.markers.indexOf(t);return-1!==r&&(s.setMap(t,null),this.markers.splice(r,1),e||this.render(),!0)}removeMarkers(t,e){let s=!1;return t.forEach((t=>{s=this.removeMarker(t,!0)||s})),s&&!e&&this.render(),s}clearMarkers(t){this.markers.length=0,t||this.render()}render(){const e=this.getMap();if(e instanceof google.maps.Map&&e.getProjection()){google.maps.event.trigger(this,t.MarkerClustererEvents.CLUSTERING_BEGIN,this);const{clusters:r,changed:o}=this.algorithm.calculate({markers:this.markers,map:e,mapCanvasProjection:this.getProjection()});if(o||null==o){const t=new Set;for(const e of r)1==e.markers.length&&t.add(e.markers[0]);const e=[];for(const r of this.clusters)null!=r.marker&&(1==r.markers.length?t.has(r.marker)||s.setMap(r.marker,null):e.push(r.marker));this.clusters=r,this.renderClusters(),requestAnimationFrame((()=>e.forEach((t=>s.setMap(t,null)))))}google.maps.event.trigger(this,t.MarkerClustererEvents.CLUSTERING_END,this)}}onAdd(){this.idleListener=this.getMap().addListener("idle",this.render.bind(this)),this.render()}onRemove(){google.maps.event.removeListener(this.idleListener),this.reset()}reset(){this.markers.forEach((t=>s.setMap(t,null))),this.clusters.forEach((t=>t.delete())),this.clusters=[]}renderClusters(){const e=new U(this.markers,this.clusters),r=this.getMap();this.clusters.forEach((o=>{1===o.markers.length?o.marker=o.markers[0]:(o.marker=this.renderer.render(o,e,r),o.markers.forEach((t=>s.setMap(t,null))),this.onClusterClick&&o.marker.addListener("click",(e=>{google.maps.event.trigger(this,t.MarkerClustererEvents.CLUSTER_CLICK,o),this.onClusterClick(e,o,r)}))),s.setMap(o.marker,r)}))}},t.MarkerUtils=s,t.NoopAlgorithm=class extends u{constructor(t){super(e(t,[]))}calculate(t){let{markers:e,map:s,mapCanvasProjection:r}=t;return{clusters:this.cluster({markers:e,map:s,mapCanvasProjection:r}),changed:!1}}cluster(t){return this.noop(t)}},t.SuperClusterAlgorithm=z,t.SuperClusterViewportAlgorithm=class extends p{constructor(t){var{maxZoom:s,radius:r=60,viewportPadding:o=60}=t,i=e(t,["maxZoom","radius","viewportPadding"]);super({maxZoom:s,viewportPadding:o}),this.superCluster=new O(Object.assign({maxZoom:this.maxZoom,radius:r},i)),this.state={zoom:-1,view:[0,0,0,0]}}calculate(t){const e={zoom:Math.round(t.map.getZoom()),view:n(t.map.getBounds(),t.mapCanvasProjection,this.viewportPadding)};let r=!f(this.state,e);if(!f(t.markers,this.markers)){r=!0,this.markers=[...t.markers];const e=this.markers.map((t=>{const e=s.getPosition(t);return{type:"Feature",geometry:{type:"Point",coordinates:[e.lng(),e.lat()]},properties:{marker:t}}}));this.superCluster.load(e)}return r&&(this.clusters=this.cluster(t),this.state=e),{clusters:this.clusters,changed:r}}cluster(t){let{map:e,mapCanvasProjection:s}=t;const r={zoom:Math.round(e.getZoom()),view:n(e.getBounds(),s,this.viewportPadding)};return this.superCluster.getClusters(r.view,r.zoom).map((t=>this.transformCluster(t)))}transformCluster(t){let{geometry:{coordinates:[e,o]},properties:i}=t;if(i.cluster)return new r({markers:this.superCluster.getLeaves(i.cluster_id,1/0).map((t=>t.properties.marker)),position:{lat:o,lng:e}});const n=i.marker;return new r({markers:[n],position:s.getPosition(n)})}},t.defaultOnClusterClickHandler=F,t.distanceBetweenPoints=a,t.extendBoundsToPaddedViewport=i,t.extendPixelBounds=l,t.filterMarkersToPaddedViewport=o,t.getPaddedViewport=n,t.noop=m,t.pixelBoundsToLatLngBounds=c,Object.defineProperty(t,"__esModule",{value:!0}),t}({});
+//# sourceMappingURL=index.min.js.map
diff --git a/license.txt b/license.txt
index 31d5833..bee264a 100644
--- a/license.txt
+++ b/license.txt
@@ -1,4 +1,4 @@
-Copyright (c) 2014 Bjorn Holine
+Copyright (c) 2016 Bjorn Holine
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
diff --git a/options.md b/options.md
index c675ddb..32bfdd5 100644
--- a/options.md
+++ b/options.md
@@ -1,80 +1,138 @@
-| Property | Default | Description |
-|---|---|---|
-| mapID | 'bh-sl-map' | ID of the div where the actual Google Map is displayed. |
-| locationList | 'bh-sl-loc-list' | Class of the container around the location list. |
-| formContainer | 'bh-sl-form-container' | Class of the container around the form. |
-| formID | 'bh-sl-user-location' | ID of the input form. |
-| addressID | 'bh-sl-address' | ID of the address input form field. |
-| regionID | 'bh-sl-region' | ID of the region input form field for country region biasing select field. |
-| mapSettings | { zoom : 12, mapTypeId: google.maps.MapTypeId.ROADMAP } | Google maps settings object. Add all settings including zoom and map type if overriding. |
-| markerImg | null | Replacement marker image used for all locations |
-| markerDim | null | Replacement marker dimensions object - ex value: { height: 20, width: 20 } |
-| catMarkers | null | Multiple replacement marker images based on categories object. Value should be array with image path followed by dimensions - ex value: catMarkers : {'Restaurant' : ['img/red-marker.svg', 32, 32]}
-| lengthUnit | 'm' | The unit of length. Default is m for miles, change to km for kilometers. |
-| storeLimit | 26 | The number of closest locations displayed at one time. Set to -1 for unlimited. |
-| distanceAlert | 60 | Displays alert if there are no locations with 60 m/km of the user's location. Set to -1 to disable. |
-| dataType | 'xml' | The format of the data source. Accepted values include kml, xml, json, and jsonp. |
-| dataLocation | 'data/locations.xml' | The path to the location data. |
-| xmlElement | 'marker' | XML element used for locations (tag). |
-| listColor1 | '#ffffff' | Background color of the odd list elements. |
-| listColor2 | '#eeeeee' | Background color of the even list elements. |
-| originMarker | false | Display a marker at the origin. |
-| originMarkerImg | null | Replacement origin marker image. |
-| originMarkerDim | null | Replacement origin marker dimensions object - ex value: { height: 20, width: 20 } |
-| bounceMarker | true | Bounces the maker when a list element is clicked. |
-| slideMap | true | First hides the map container and then uses jQuery’s slideDown method to reveal the map. |
-| modal | false | Shows the map container within a modal window. Set slideMap to false and this option to true to use. |
-| overlay | 'bh-sl-overlay' | Class of element that fills 100% of the window and fills with a transparent background image. |
-| modalWindow | 'bh-sl-modal-window' | Class of element of the actual modal window |
-| modalContent | 'bh-sl-modal-content' | Class of element container around the content of the modal window. |
-| closeIcon| 'bh-sl-close-icon' | Class of element that displays the close icon to close the modal window. |
-| defaultLoc | false | If true, the map will load with a default location immediately. Set slideMap to false if you want to use this. |
-| defaultLat | null | If using defaultLoc, set this to the default location latitude. |
-| defaultLng | null | If using defaultLoc, set this to the default location longitude. |
-| autoGeocode | false | Set to true if you want to use the HTML5 geolocation API (good for mobile) to geocode the user's location. |
-| maxDistance | false | Set to true if you want to give users an option to limit the distance from their location to the markers. |
-| maxDistanceID | 'maxdistance' | ID of the select element for the maximum distance options. |
-| fullMapStart | false | Set to true if you want to immediately show a map of all locations. The map will center and zoom automatically. |
-| noForm | false | Set to true if you aren't able to use form tags (ASP.net WebForms). |
-| loading | false | Set to true to display a loading animated gif next to the submit button. |
-| loadingContainer | 'bh-sl-loading' | Class of element container that displays the loading animated gif. |
-| featuredLocations | false | Set to true to enable featuring locations at the top of the location list (no matter the distance). Add featured=”true” to featured locations in your XML or JSON locations data. |
-| pagination | false | Set to true to enable displaying location results in multiple "pages." |
-| locationsPerPage | 10 | If using pagination, the number of locations to display per page. |
-| inlineDirections | false | Set to true to enable displaying directions within the app instead of an off-site link. |
-| nameSearch | false | Set to true to allow searching for locations by name using separate searchID field. |
-| searchID | 'bh-sl-search' | ID of the search input form field for location name searching. |
-| nameAttribute | 'name' | If using nameSearch, the data attribute used for the location name in the data file. |
-| infowindowTemplatePath | 'templates/infowindow-description.html' | Path to the default infowindow template. |
-| listTemplatePath | 'templates/location-list-description.html' | Path to the default list template. |
-| KMLinfowindowTemplatePath | 'templates/kml-infowindow-description.html' | Path to the KML infowindow template – used if dataType is set to kml. |
-| KMLlistTemplatePath | 'templates/kml-location-list-description.html' | Path to the KML list template – used if dataType is set to kml. |
-| listTemplateID | null | ID of list template if using inline Handlebar templates instead of separate files. |
-| infowindowTemplateID | null | ID of infowindow template if using inline Handlebar templates instead of separate files. |
-| taxonomyFilters | null | Filtering object that can be used to set up live filtering (see categories example). |
-| taxonomyFiltersContainer | 'bh-sl-filters-container' | Class of the container around the filters. |
-| querystringParams | false | Set to true to enable query string support for passing input variables from page to page. |
-| callbackNotify | null | Callback that can override the notify method. |
-| callbackBeforeSend | null | Callback that fires before the AJAX request. |
-| callbackSuccess | null | Callback that fires on successful AJAX request. |
-| callbackModalOpen | null | Callback that fires when a modal opens. |
-| callbackModalReady | null | Callback that fires when the content of the modal is generated. |
-| callbackModalClose | null | Callback that fires when a modal closes. |
-| callbackJsonp | null | Callback that can specify the callback function name of a JSONP request. |
-| callbackPageChange | null | Callback that fires when the page changes if pagination is enabled. |
-| callbackDirectionsRequest | null | Callback that fires upon a directions request when using the inline directions option. |
-| callbackCloseDirections | null | Callback that fires when the directions panel closes. |
-| callbackNoResults | null | Callback that fires when no results are found. |
-| callbackListClick | null | Callback that fires when a list element is clicked in the location list. |
-| callbackMarkerClick | null | Callback that fires when a map marker is clicked. |
-| addressErrorAlert | 'Unable to find address' | Language setting |
-| autoGeocodeErrorAlert | 'Automatic location detection failed. Please fill in your address or zip code.' | Language setting |
-| distanceErrorAlert | 'Unfortunately, our closest location is more than ' | Language setting |
-| mileLang | 'mile' | Language setting |
-| milesLang | 'miles' | Language setting |
-| kilometerLang | 'kilometer' | Language setting |
-| kilometersLang | 'kilometers' | Language setting |
-| noResultsTitle | 'No results' | Language setting |
-| noResultsDesc | 'No locations were found with the given criteria. Please modify your selections or input.' | Language setting |
-| nextPage | 'Next »' | Language setting |
-| prevPage | '« Prev' | Language setting |
\ No newline at end of file
+## Standard settings
+
+| Property | Default | Description |
+|------------------------------|---------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| ajaxData | null | Allows custom data to be sent with the AJAX request. Set the setting to an object with your properties and values. |
+| altDistanceNoResult | false | Display no results message vs. all locations when closest location is further than distanceAlert setting. |
+| apiKey | null | Set to your Google Maps API key when using Lazy Load setting. Value is unused if lazyLoadMap is false. |
+| autoComplete | false | Set to true to enable Google Places autocomplete. Note the slight markup differences in the example file. |
+| autoCompleteDisableListener | false | Disable the listener that immediately triggers a search when an auto complete location option is selected. |
+| autoCompleteOptions | {} | Google Places autocomplete [options object](https://developers.google.com/maps/documentation/javascript/places-autocomplete#add_autocomplete). |
+| autoGeocode | false | Set to true if you want to use the HTML5 geolocation API (good for mobile) to geocode the user's location. **SSL is required**. |
+| bounceMarker | true | Deprecated - no longer applies. |
+| catMarkers | null | Multiple replacement marker images based on categories object. Value should be array with image path followed by dimensions - ex value: catMarkers : {'Restaurant' : ['img/red-marker.svg', 32, 32]} |
+| dataLocation | 'data/locations.json' | The path to the location data. |
+| dataRaw | null | Accepts raw KML, XML, or JSON instead of using a remote file. |
+| dataType | 'json' | The format of the data source. Accepted values include kml, xml, json, and jsonp. |
+| debug | false | Set to true to enable console.log helper function that can be used for debugging. |
+| defaultLat | null | If using defaultLoc, set this to the default location latitude. |
+| defaultLng | null | If using defaultLoc, set this to the default location longitude. |
+| defaultLoc | false | If true, the map will load with a default location immediately. Set slideMap to false if you want to use this. |
+| disableAlphaMarkers | false | Disable displaying markers and location list indicators with alpha characters. |
+| distanceAlert | 60 | Displays alert if there are no locations with 60 m/km of the user's location. Set to -1 to disable. |
+| dragSearch | false | Set to true to perform a new search after the map is dragged. |
+| exclusiveFiltering | false | Set to true to enable exclusive taxonomy filtering rather than the default inclusive. |
+| exclusiveTax | null | Set to an array of taxonomies that should filter exclusively vs. inclusively. |
+| featuredDistance | null | Restrict the featured locations from displaying by a certain distance (use number value). |
+| featuredLocations | false | Set to true to enable featuring locations at the top of the location list (no matter the distance). Add featured=”true” to featured locations in your XML or JSON locations data. |
+| fullMapStart | false | Set to true if you want to immediately show a map of all locations. The map will center and zoom automatically. |
+| fullMapStartBlank | false | Set to a zoom integer if you want to immediately show a blank map without any locations. |
+| fullMapStartListLimit | false | Set to a number to limit the number of items displayed in the location list with full map start. |
+| infoBubble | null | InfoBubble settings object. [See example for available parameters](https://googlemaps.github.io/js-info-bubble/examples/example.html). Map and content parameters are set by default. |
+| inlineDirections | false | Set to true to enable displaying directions within the app instead of an off-site link. |
+| lazyLoadMap | false | Set to true to lazy load the Google Maps API script. Make sure to also set the apiKey setting and do not include the maps.google.com script manually. |
+| lengthUnit | 'm' | The unit of length. Default is m for miles, change to km for kilometers. |
+| listColor1 | '#ffffff' | Background color of the odd list elements. |
+| listColor2 | '#eeeeee' | Background color of the even list elements. |
+| loading | false | Set to true to display a loading animated gif next to the submit button. |
+| locationsPerPage | 10 | If using pagination, the number of locations to display per page. |
+| mapSettings | { zoom : 12, mapTypeId: google.maps.MapTypeId.ROADMAP } | Google maps settings object. Add all settings including zoom and map type if overriding. Set zoom to 0 to automatically center and zoom to show all display markers on the map |
+| mapSettingsID | '' | New Google Map ID used for map management and styling. Please refer to [Use Map IDs](https://developers.google.com/maps/documentation/get-map-id) and [Map ID with Styling](https://developers.google.com/maps/documentation/javascript/examples/map-id-style) in the Google API docs. |
+| markerCluster | null | Additional Clusterer settings object. Markers and map are set by default - use this to set other parameters such as algorithm, renderer, etc. [See docs](https://github.com/googlemaps/js-markerclusterer?tab=readme-ov-file#migration). |
+| markerImg | null | Replacement marker image used for all locations |
+| markerDim | null | Replacement marker dimensions object - ex value: { height: 20, width: 20 } |
+| maxDistance | false | Set to true if you want to give users an option to limit the distance from their location to the markers. |
+| modal | false | Shows the map container within a modal window. Set slideMap to false and this option to true to use. |
+| nameAttribute | 'name' | If using nameSearch, the data attribute used for the location name in the data file. Supports one or multiple values separated by commas. |
+| nameSearch | false | Set to true to allow searching for locations by name using separate searchID field. |
+| noForm | false | Set to true if you aren't able to use form tags (ASP.net WebForms). |
+| openNearest | false | Set to true to highlight the nearest location automatically after searching. |
+| originMarker | false | Display a marker at the origin. |
+| originMarkerDim | null | Replacement origin marker dimensions object - ex value: { height: 20, width: 20 } |
+| originMarkerImg | null | Replacement origin marker image. |
+| pagination | false | Set to true to enable displaying location results in multiple "pages." |
+| querystringParams | false | Set to true to enable query string support for passing input variables from page to page. |
+| selectedMarkerImg | null | Selected marker image. |
+| selectedMarkerImgDim | null | Selected marker image dimensions object - ex value: { height: 20, width: 20 } |
+| sessionStorage | false | Set to true to enable Window.sessionStorage for user's location when autoGeocode is enabled. |
+| slideMap | true | First hides the map container and then uses jQuery’s slideDown method to reveal the map. |
+| sortBy | null | Set to an object for custom sorting that accepts three properties: method ('alpha', 'date', or 'numeric'), order ('asc', or 'desc'), and prop (property in your data to sort by such as name, city, distance, etc.). |
+| storeLimit | 26 | The number of closest locations displayed at one time. Set to -1 for unlimited. |
+| taxonomyFilters | null | Filtering object that can be used to set up live filtering (see categories example). |
+| visibleMarkersList | false | Set to true to have the location list only show data from markers that are visible on the map. |
+| xmlElement | 'marker' | XML element used for locations (tag). |
+
+## HTML elements
+| Property | Default | Description |
+|--------------------------|---------------------------|-----------------------------------------------------------------------------------------------------------------------------------------|
+| addressID | 'bh-sl-address' | ID of the address input form field. |
+| closeIcon | 'bh-sl-close-icon' | Class of element that displays the close icon to close the modal window. |
+| formContainer | 'bh-sl-form-container' | Class of the container around the form. |
+| formID | 'bh-sl-user-location' | ID of the input form. |
+| geocodeID | null | Set to the ID of an element to connect the HTML5 geolocation API to a button instead of firing automatically. |
+| lengthSwapID | 'bh-sl-length-swap' | Set to the ID of a select element within the form container to allow users to swap between the distance length unit (miles/kilometers). |
+| loadingContainer | 'bh-sl-loading' | Class of element container that displays the loading animated gif. |
+| locationList | 'bh-sl-loc-list' | Class of the container around the location list. |
+| mapID | 'bh-sl-map' | ID of the div where the actual Google Map is displayed. |
+| maxDistanceID | 'bh-sl-maxdistance' | ID of the select element for the maximum distance options. |
+| modalContent | 'bh-sl-modal-content' | Class of element container around the content of the modal window. |
+| modalWindow | 'bh-sl-modal-window' | Class of element of the actual modal window |
+| orderID | 'bh-sl-order' | ID of the select form field for custom sort order handling of location results. |
+| overlay | 'bh-sl-overlay' | Class of element that fills 100% of the window and fills with a transparent background image. |
+| regionID | 'bh-sl-region' | ID of the region select form field for country region biasing. |
+| searchID | 'bh-sl-search' | ID of the search input form field for location name searching. |
+| sortID | 'bh-sl-sort' | ID of the select form field for custom sorting of location results. |
+| taxonomyFiltersContainer | 'bh-sl-filters-container' | Class of the container around the filters. |
+
+## Templates
+| Property | Default | Description |
+|---------------------------|-------------------------------------------------------------------------------|------------------------------------------------------------------------------------------|
+| infowindowTemplatePath | 'assets/js/plugins/storeLocator/templates/infowindow-description.html' | Path to the default infowindow template. |
+| listTemplatePath | 'assets/js/plugins/storeLocator/templates/location-list-description.html' | Path to the default list template. |
+| KMLinfowindowTemplatePath | 'assets/js/plugins/storeLocator/templates/kml-infowindow-description.html' | Path to the KML infowindow template – used if dataType is set to kml. |
+| KMLlistTemplatePath | 'assets/js/plugins/storeLocator/templates/kml-location-list-description.html' | Path to the KML list template – used if dataType is set to kml. |
+| listTemplateID | null | ID of list template if using inline Handlebar templates instead of separate files. |
+| infowindowTemplateID | null | ID of infowindow template if using inline Handlebar templates instead of separate files. |
+
+## Callbacks
+
+| Property | Default | Description |
+|---------------------------------------------------------------------------|---------|-------------------------------------------|
+| [callbackAutoGeoSuccess](callbacks/callback-autogeosuccess.md) | null | Geolocation API success callback |
+| [callbackBeforeSend](callbacks/callback-beforesend.md) | null | Before location data request callback |
+| [callbackCloseDirections](callbacks/callback-closedirections.md) | null | Close directions callback |
+| [callbackCreateMarker](callbacks/callback-createmarker.md) | null | Create marker override callback |
+| [callbackDirectionsRequest](callbacks/callback-directionsrequest.md) | null | Directions request callback |
+| [callbackFilters](callbacks/callback-filters.md) | null | Filters callback |
+| [callbackFormVals](callbacks/callback-formvals.md) | null | Form values callback |
+| [callbackGeocodeRestrictions](callbacks/callback-geocode-restrictions.md) | null | Geocoding component restrictions callback |
+| [callbackJsonp](callbacks/callback-jsonp.md) | null | JSONP callback |
+| [callbackListClick](callbacks/callback-listclick.md) | null | Location list click callback |
+| [callbackMapSet](callbacks/callback-mapset.md) | null | Map set callback |
+| [callbackMarkerClick](callbacks/callback-markerclick.md) | null | Marker click callback |
+| [callbackModalClose](callbacks/callback-modalclose.md) | null | Modal close callback |
+| [callbackModalOpen](callbacks/callback-modalopen.md) | null | Modal open callback |
+| [callbackModalReady](callbacks/callback-modalready.md) | null | Modal ready callback |
+| [callbackNearestLoc](callbacks/callback-nearestloc.md) | null | Nearest location callback |
+| [callbackNoResults](callbacks/callback-noresults.md) | null | No results callback |
+| [callbackNotify](callbacks/callback-notification.md) | null | Notification callback |
+| [callbackOrder](callbacks/callback-order.md) | null | Order callback |
+| [callbackPageChange](callbacks/callback-pagechange.md) | null | Page change callback |
+| [callbackRegion](callbacks/callback-region.md) | null | Region callback |
+| [callbackSorting](callbacks/callback-sorting.md) | null | Sorting callback |
+| [callbackSuccess](callbacks/callback-success.md) | null | Success callback |
+
+## Language options
+| Property | Default | Description |
+|-----------------------|--------------------------------------------------------------------------------------------|------------------|
+| addressErrorAlert | 'Unable to find address' | Language setting |
+| autoGeocodeErrorAlert | 'Automatic location detection failed. Please fill in your address or zip code.' | Language setting |
+| distanceErrorAlert | 'Unfortunately, our closest location is more than ' | Language setting |
+| kilometerLang | 'kilometer' | Language setting |
+| kilometersLang | 'kilometers' | Language setting |
+| mileLang | 'mile' | Language setting |
+| milesLang | 'miles' | Language setting |
+| noResultsTitle | 'No results' | Language setting |
+| noResultsDesc | 'No locations were found with the given criteria. Please modify your selections or input.' | Language setting |
+| nextPage | 'Next »' | Language setting |
+| prevPage | '« Prev' | Language setting |
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..eb1502a
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,2872 @@
+{
+ "name": "jquery-storelocator-plugin",
+ "version": "3.4.1",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "jquery-storelocator-plugin",
+ "version": "3.4.1",
+ "license": "MIT",
+ "dependencies": {
+ "handlebars": ">=4.7.7",
+ "jquery": "^3.6.3"
+ },
+ "devDependencies": {
+ "grunt": "~1.6.1",
+ "grunt-banner": "~0.6.0",
+ "grunt-contrib-clean": "^2.0.1",
+ "grunt-contrib-concat": "~2.1.0",
+ "grunt-contrib-copy": "^1.0.0",
+ "grunt-contrib-cssmin": "~4.0.0",
+ "grunt-contrib-handlebars": "^3.0.0",
+ "grunt-contrib-jshint": "~3.2.0",
+ "grunt-contrib-qunit": "~7.0.0",
+ "grunt-contrib-sass": "^2.0.0",
+ "grunt-contrib-uglify": "~5.2.2",
+ "grunt-contrib-watch": "~1.1.0"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
+ "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/highlight": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.19.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
+ "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/highlight": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
+ "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.18.6",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "1.1.3"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+ "dev": true
+ },
+ "node_modules/@babel/highlight/node_modules/has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "18.14.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.0.tgz",
+ "integrity": "sha512-5EWrvLmglK+imbCJY0+INViFWUHg1AHel1sq4ZVSfdcNqGy9Edv3UB9IIzzg+xPaUcAgZYcfVs2fBcwDeZzU0A==",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/@types/yauzl": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz",
+ "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/abbrev": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+ "dev": true
+ },
+ "node_modules/agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "dev": true,
+ "dependencies": {
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "node_modules/argparse/node_modules/sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
+ "dev": true
+ },
+ "node_modules/array-each": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz",
+ "integrity": "sha512-zHjL5SZa68hkKHBFBK6DJCTtr9sfTCPCaph/L7tMSLcTFgy+zX7E+6q5UArbtOtMBCtxdICpfTCspRse+ywyXA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/array-slice": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz",
+ "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/async": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz",
+ "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==",
+ "dev": true
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+ "dev": true
+ },
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/bl": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
+ "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+ "dev": true,
+ "dependencies": {
+ "buffer": "^5.5.0",
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.4.0"
+ }
+ },
+ "node_modules/bl/node_modules/readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "dev": true,
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/bl/node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/body": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/body/-/body-5.1.0.tgz",
+ "integrity": "sha1-5LoM5BCkaTYyM2dgnstOZVMSUGk=",
+ "dev": true,
+ "dependencies": {
+ "continuable-cache": "^0.3.1",
+ "error": "^7.0.0",
+ "raw-body": "~1.1.0",
+ "safe-json-parse": "~1.0.1"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "dependencies": {
+ "fill-range": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.1.13"
+ }
+ },
+ "node_modules/buffer-crc32": {
+ "version": "0.2.13",
+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+ "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/bytes": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz",
+ "integrity": "sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g=",
+ "dev": true
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/chownr": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+ "dev": true
+ },
+ "node_modules/clean-css": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz",
+ "integrity": "sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==",
+ "dev": true,
+ "dependencies": {
+ "source-map": "~0.6.0"
+ },
+ "engines": {
+ "node": ">= 10.0"
+ }
+ },
+ "node_modules/cli": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz",
+ "integrity": "sha512-41U72MB56TfUMGndAKK8vJ78eooOD4Z5NOL4xEfjc0c23s+6EYKXlXsmACBVclLP1yOfWCgEganVzddVrSNoTg==",
+ "dev": true,
+ "dependencies": {
+ "exit": "0.1.2",
+ "glob": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=0.2.5"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/colors": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
+ "integrity": "sha512-ENwblkFQpqqia6b++zLD/KUWafYlVY/UNnAp7oz7LY7E924wmpye416wBOmvv/HMWzl8gL1kJlfvId/1Dg176w==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.1.90"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "node_modules/console-browserify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
+ "integrity": "sha512-duS7VP5pvfsNLDvL1O4VOEbw37AI3A4ZUQYemvDlnpGrNu9tprR7BYWpDYwC0Xia0Zxz5ZupdiIrUp0GH1aXfg==",
+ "dev": true,
+ "dependencies": {
+ "date-now": "^0.1.4"
+ }
+ },
+ "node_modules/continuable-cache": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/continuable-cache/-/continuable-cache-0.3.1.tgz",
+ "integrity": "sha1-vXJ6f67XfnH/OYWskzUakSczrQ8=",
+ "dev": true
+ },
+ "node_modules/core-util-is": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+ "dev": true
+ },
+ "node_modules/cosmiconfig": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.0.0.tgz",
+ "integrity": "sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ==",
+ "dev": true,
+ "dependencies": {
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "parse-json": "^5.0.0",
+ "path-type": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/cosmiconfig/node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
+ "node_modules/cosmiconfig/node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/cross-fetch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz",
+ "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==",
+ "dev": true,
+ "dependencies": {
+ "node-fetch": "2.6.7"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+ "dev": true,
+ "dependencies": {
+ "nice-try": "^1.0.4",
+ "path-key": "^2.0.1",
+ "semver": "^5.5.0",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ },
+ "engines": {
+ "node": ">=4.8"
+ }
+ },
+ "node_modules/dargs": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/dargs/-/dargs-6.1.0.tgz",
+ "integrity": "sha512-5dVBvpBLBnPwSsYXqfybFyehMmC/EenKEcf23AhCTgTf48JFBbmJKqoZBsERDnjL0FyiVTYWdFsRfTLHxLyKdQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/date-now": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
+ "integrity": "sha512-AsElvov3LoNB7tf5k37H2jYSB+ZZPMT5sG2QjJCcdlV5chIv6htBUBUui2IKRjgtKAKtCBN7Zbwa+MtwLjSeNw==",
+ "dev": true
+ },
+ "node_modules/dateformat": {
+ "version": "4.6.3",
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz",
+ "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/detect-file": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
+ "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/devtools-protocol": {
+ "version": "0.0.1094867",
+ "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1094867.tgz",
+ "integrity": "sha512-pmMDBKiRVjh0uKK6CT1WqZmM3hBVSgD+N2MrgyV1uNizAZMw4tx6i/RTc+/uCsKSCmg0xXx7arCP/OFcIwTsiQ==",
+ "dev": true
+ },
+ "node_modules/dom-serializer": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz",
+ "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==",
+ "dev": true,
+ "dependencies": {
+ "domelementtype": "^2.0.1",
+ "entities": "^2.0.0"
+ }
+ },
+ "node_modules/dom-serializer/node_modules/domelementtype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ]
+ },
+ "node_modules/dom-serializer/node_modules/entities": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+ "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/domelementtype": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
+ "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==",
+ "dev": true
+ },
+ "node_modules/domhandler": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz",
+ "integrity": "sha512-q9bUwjfp7Eif8jWxxxPSykdRZAb6GkguBGSgvvCrhI9wB71W2K/Kvv4E61CF/mcCfnVJDeDWx/Vb/uAqbDj6UQ==",
+ "dev": true,
+ "dependencies": {
+ "domelementtype": "1"
+ }
+ },
+ "node_modules/domutils": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
+ "integrity": "sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==",
+ "dev": true,
+ "dependencies": {
+ "dom-serializer": "0",
+ "domelementtype": "1"
+ }
+ },
+ "node_modules/duplexer": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
+ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==",
+ "dev": true
+ },
+ "node_modules/end-of-stream": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+ "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.4.0"
+ }
+ },
+ "node_modules/entities": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz",
+ "integrity": "sha512-LbLqfXgJMmy81t+7c14mnulFHJ170cM6E+0vMXR9k/ZiZwgX8i5pNgjTCX3SO4VeUsFLV+8InixoretwU+MjBQ==",
+ "dev": true
+ },
+ "node_modules/error": {
+ "version": "7.2.1",
+ "resolved": "https://registry.npmjs.org/error/-/error-7.2.1.tgz",
+ "integrity": "sha512-fo9HBvWnx3NGUKMvMwB/CBCMMrfEJgbDTVDEkPygA3Bdd3lM1OyCd+rbQ8BwnpF6GdVeOLDNmyL4N5Bg80ZvdA==",
+ "dev": true,
+ "dependencies": {
+ "string-template": "~0.2.1"
+ }
+ },
+ "node_modules/error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dev": true,
+ "dependencies": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true,
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/eventemitter2": {
+ "version": "0.4.14",
+ "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz",
+ "integrity": "sha512-K7J4xq5xAD5jHsGM5ReWXRTFa3JRGofHiMcVgQ8PRwgWxzjHpMWCIzsmyf60+mh8KLsqYPcjUMa0AC4hd6lPyQ==",
+ "dev": true
+ },
+ "node_modules/exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+ "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/expand-tilde": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
+ "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==",
+ "dev": true,
+ "dependencies": {
+ "homedir-polyfill": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "dev": true
+ },
+ "node_modules/extract-zip": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
+ "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^4.1.1",
+ "get-stream": "^5.1.0",
+ "yauzl": "^2.10.0"
+ },
+ "bin": {
+ "extract-zip": "cli.js"
+ },
+ "engines": {
+ "node": ">= 10.17.0"
+ },
+ "optionalDependencies": {
+ "@types/yauzl": "^2.9.1"
+ }
+ },
+ "node_modules/faye-websocket": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz",
+ "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=",
+ "dev": true,
+ "dependencies": {
+ "websocket-driver": ">=0.5.1"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/fd-slicer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
+ "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
+ "dev": true,
+ "dependencies": {
+ "pend": "~1.2.0"
+ }
+ },
+ "node_modules/figures": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
+ "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
+ "dev": true,
+ "dependencies": {
+ "escape-string-regexp": "^1.0.5"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/file-sync-cmp": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/file-sync-cmp/-/file-sync-cmp-0.1.1.tgz",
+ "integrity": "sha512-0k45oWBokCqh2MOexeYKpyqmGKG+8mQ2Wd8iawx+uWd/weWJQAZ6SoPybagdCI4xFisag8iAR77WPm4h3pTfxA==",
+ "dev": true
+ },
+ "node_modules/fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/findup-sync": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-5.0.0.tgz",
+ "integrity": "sha512-MzwXju70AuyflbgeOhzvQWAvvQdo1XL0A9bVvlXsYcFEBM87WR4OakL4OfZq+QRmr+duJubio+UtNQCPsVESzQ==",
+ "dev": true,
+ "dependencies": {
+ "detect-file": "^1.0.0",
+ "is-glob": "^4.0.3",
+ "micromatch": "^4.0.4",
+ "resolve-dir": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ }
+ },
+ "node_modules/fined": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz",
+ "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==",
+ "dev": true,
+ "dependencies": {
+ "expand-tilde": "^2.0.2",
+ "is-plain-object": "^2.0.3",
+ "object.defaults": "^1.1.0",
+ "object.pick": "^1.2.0",
+ "parse-filepath": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/flagged-respawn": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz",
+ "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/for-in": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
+ "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/for-own": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz",
+ "integrity": "sha512-0OABksIGrxKK8K4kynWkQ7y1zounQxP+CWnyclVwj81KW3vlLlGUx57DKGcP/LH216GzqnstnPocF16Nxs0Ycg==",
+ "dev": true,
+ "dependencies": {
+ "for-in": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fs-constants": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
+ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
+ "dev": true
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "node_modules/gaze": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz",
+ "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==",
+ "dev": true,
+ "dependencies": {
+ "globule": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
+ "node_modules/get-stream": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+ "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+ "dev": true,
+ "dependencies": {
+ "pump": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/getobject": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/getobject/-/getobject-1.0.2.tgz",
+ "integrity": "sha512-2zblDBaFcb3rB4rF77XVnuINOE2h2k/OnqXAiy0IrTxUfV1iFp3la33oAQVY9pCpWU268WFYVt2t71hlMuLsOg==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/glob": {
+ "version": "7.1.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+ "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/global-modules": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
+ "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
+ "dev": true,
+ "dependencies": {
+ "global-prefix": "^1.0.1",
+ "is-windows": "^1.0.1",
+ "resolve-dir": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/global-prefix": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz",
+ "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==",
+ "dev": true,
+ "dependencies": {
+ "expand-tilde": "^2.0.2",
+ "homedir-polyfill": "^1.0.1",
+ "ini": "^1.3.4",
+ "is-windows": "^1.0.1",
+ "which": "^1.2.14"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/globule": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.2.tgz",
+ "integrity": "sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA==",
+ "dev": true,
+ "dependencies": {
+ "glob": "~7.1.1",
+ "lodash": "~4.17.10",
+ "minimatch": "~3.0.2"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/grunt": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.6.1.tgz",
+ "integrity": "sha512-/ABUy3gYWu5iBmrUSRBP97JLpQUm0GgVveDCp6t3yRNIoltIYw7rEj3g5y1o2PGPR2vfTRGa7WC/LZHLTXnEzA==",
+ "dev": true,
+ "dependencies": {
+ "dateformat": "~4.6.2",
+ "eventemitter2": "~0.4.13",
+ "exit": "~0.1.2",
+ "findup-sync": "~5.0.0",
+ "glob": "~7.1.6",
+ "grunt-cli": "~1.4.3",
+ "grunt-known-options": "~2.0.0",
+ "grunt-legacy-log": "~3.0.0",
+ "grunt-legacy-util": "~2.0.1",
+ "iconv-lite": "~0.6.3",
+ "js-yaml": "~3.14.0",
+ "minimatch": "~3.0.4",
+ "nopt": "~3.0.6"
+ },
+ "bin": {
+ "grunt": "bin/grunt"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/grunt-banner": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/grunt-banner/-/grunt-banner-0.6.0.tgz",
+ "integrity": "sha512-50H/Wxydlf+ifve5Jzcz9oB4jr6oCGEPyfhEDUsl2NEMX80cWUJqVMXSHBr2n9Rb3nd+rRSKeQzqNxWrqoyQ1A==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "peerDependencies": {
+ "grunt": ">=0.4.0"
+ }
+ },
+ "node_modules/grunt-banner/node_modules/ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/grunt-banner/node_modules/chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/grunt-banner/node_modules/supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/grunt-contrib-clean": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-clean/-/grunt-contrib-clean-2.0.1.tgz",
+ "integrity": "sha512-uRvnXfhiZt8akb/ZRDHJpQQtkkVkqc/opWO4Po/9ehC2hPxgptB9S6JHDC/Nxswo4CJSM0iFPT/Iym3cEMWzKA==",
+ "dev": true,
+ "dependencies": {
+ "async": "^3.2.3",
+ "rimraf": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "peerDependencies": {
+ "grunt": ">=0.4.5"
+ }
+ },
+ "node_modules/grunt-contrib-concat": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-concat/-/grunt-contrib-concat-2.1.0.tgz",
+ "integrity": "sha512-Vnl95JIOxfhEN7bnYIlCgQz41kkbi7tsZ/9a4usZmxNxi1S2YAIOy8ysFmO8u4MN26Apal1O106BwARdaNxXQw==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.1.2",
+ "source-map": "^0.5.3"
+ },
+ "engines": {
+ "node": ">=0.12.0"
+ },
+ "peerDependencies": {
+ "grunt": ">=1.4.1"
+ }
+ },
+ "node_modules/grunt-contrib-concat/node_modules/source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/grunt-contrib-copy": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-copy/-/grunt-contrib-copy-1.0.0.tgz",
+ "integrity": "sha512-gFRFUB0ZbLcjKb67Magz1yOHGBkyU6uL29hiEW1tdQ9gQt72NuMKIy/kS6dsCbV0cZ0maNCb0s6y+uT1FKU7jA==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^1.1.1",
+ "file-sync-cmp": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/grunt-contrib-copy/node_modules/ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/grunt-contrib-copy/node_modules/chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/grunt-contrib-copy/node_modules/supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/grunt-contrib-cssmin": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-cssmin/-/grunt-contrib-cssmin-4.0.0.tgz",
+ "integrity": "sha512-jXU+Zlk8Q8XztOGNGpjYlD/BDQ0n95IHKrQKtFR7Gd8hZrzgqiG1Ra7cGYc8h2DD9vkSFGNlweb9Q00rBxOK2w==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.1.0",
+ "clean-css": "^5.0.1",
+ "maxmin": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10.0"
+ }
+ },
+ "node_modules/grunt-contrib-handlebars": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-handlebars/-/grunt-contrib-handlebars-3.0.0.tgz",
+ "integrity": "sha512-Zh5fSnyhfOGIDieFNy1eVEqmdB0y2cGkFaceKkfJM4v5OEZP880+SGjbmmuriaVZsG7bOnj0Fg7wP4722GUBSw==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.1.1",
+ "handlebars": "^4.7.7",
+ "nsdeclare": "0.1.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/grunt-contrib-jshint": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-3.2.0.tgz",
+ "integrity": "sha512-pcXWCSZWfoMSvcV4BwH21TUtLtcX0Ms8IGuOPIcLeXK3fud9KclY7iqMKY94jFx8TxZzh028YYtpR+io8DiEaQ==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "~4.1.2",
+ "hooker": "^0.2.3",
+ "jshint": "~2.13.4"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/grunt-contrib-qunit": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-qunit/-/grunt-contrib-qunit-7.0.0.tgz",
+ "integrity": "sha512-phSuAixAzyvizgUV9Nw7ip0/G5lVeWA/4DUifcdKPOgjTAY7QIWsTugxiXMGeVwMKnDOS3vTMpL/VyEw8z7yYw==",
+ "dev": true,
+ "dependencies": {
+ "eventemitter2": "^6.4.9",
+ "p-each-series": "^2.2.0",
+ "puppeteer": "^19.7.0"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/grunt-contrib-qunit/node_modules/eventemitter2": {
+ "version": "6.4.9",
+ "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz",
+ "integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==",
+ "dev": true
+ },
+ "node_modules/grunt-contrib-sass": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-sass/-/grunt-contrib-sass-2.0.0.tgz",
+ "integrity": "sha512-RxZ3dlZZTX4YBPu2zMu84NPYgJ2AYAlIdEqlBaixNVyLNbgvJBGUr5Gi0ec6IiOQbt/I/z7uZVN9HsRxgznIRw==",
+ "dev": true,
+ "dependencies": {
+ "async": "^2.6.1",
+ "chalk": "^2.4.1",
+ "cross-spawn": "^6.0.5",
+ "dargs": "^6.0.0",
+ "which": "^1.3.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/grunt-contrib-sass/node_modules/ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/grunt-contrib-sass/node_modules/async": {
+ "version": "2.6.3",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
+ "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
+ "dev": true,
+ "dependencies": {
+ "lodash": "^4.17.14"
+ }
+ },
+ "node_modules/grunt-contrib-sass/node_modules/chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/grunt-contrib-sass/node_modules/color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "1.1.3"
+ }
+ },
+ "node_modules/grunt-contrib-sass/node_modules/color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "node_modules/grunt-contrib-sass/node_modules/has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/grunt-contrib-sass/node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/grunt-contrib-uglify": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-uglify/-/grunt-contrib-uglify-5.2.2.tgz",
+ "integrity": "sha512-ITxiWxrjjP+RZu/aJ5GLvdele+sxlznh+6fK9Qckio5ma8f7Iv8woZjRkGfafvpuygxNefOJNc+hfjjBayRn2Q==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.1.2",
+ "maxmin": "^3.0.0",
+ "uglify-js": "^3.16.1",
+ "uri-path": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/grunt-contrib-uglify/node_modules/uglify-js": {
+ "version": "3.17.4",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz",
+ "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==",
+ "dev": true,
+ "bin": {
+ "uglifyjs": "bin/uglifyjs"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/grunt-contrib-watch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-1.1.0.tgz",
+ "integrity": "sha512-yGweN+0DW5yM+oo58fRu/XIRrPcn3r4tQx+nL7eMRwjpvk+rQY6R8o94BPK0i2UhTg9FN21hS+m8vR8v9vXfeg==",
+ "dev": true,
+ "dependencies": {
+ "async": "^2.6.0",
+ "gaze": "^1.1.0",
+ "lodash": "^4.17.10",
+ "tiny-lr": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/grunt-contrib-watch/node_modules/async": {
+ "version": "2.6.3",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
+ "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
+ "dev": true,
+ "dependencies": {
+ "lodash": "^4.17.14"
+ }
+ },
+ "node_modules/grunt-known-options": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-2.0.0.tgz",
+ "integrity": "sha512-GD7cTz0I4SAede1/+pAbmJRG44zFLPipVtdL9o3vqx9IEyb7b4/Y3s7r6ofI3CchR5GvYJ+8buCSioDv5dQLiA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/grunt-legacy-log": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-3.0.0.tgz",
+ "integrity": "sha512-GHZQzZmhyq0u3hr7aHW4qUH0xDzwp2YXldLPZTCjlOeGscAOWWPftZG3XioW8MasGp+OBRIu39LFx14SLjXRcA==",
+ "dev": true,
+ "dependencies": {
+ "colors": "~1.1.2",
+ "grunt-legacy-log-utils": "~2.1.0",
+ "hooker": "~0.2.3",
+ "lodash": "~4.17.19"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ }
+ },
+ "node_modules/grunt-legacy-log-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-2.1.0.tgz",
+ "integrity": "sha512-lwquaPXJtKQk0rUM1IQAop5noEpwFqOXasVoedLeNzaibf/OPWjKYvvdqnEHNmU+0T0CaReAXIbGo747ZD+Aaw==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "~4.1.0",
+ "lodash": "~4.17.19"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/grunt-legacy-util": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-2.0.1.tgz",
+ "integrity": "sha512-2bQiD4fzXqX8rhNdXkAywCadeqiPiay0oQny77wA2F3WF4grPJXCvAcyoWUJV+po/b15glGkxuSiQCK299UC2w==",
+ "dev": true,
+ "dependencies": {
+ "async": "~3.2.0",
+ "exit": "~0.1.2",
+ "getobject": "~1.0.0",
+ "hooker": "~0.2.3",
+ "lodash": "~4.17.21",
+ "underscore.string": "~3.3.5",
+ "which": "~2.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/grunt-legacy-util/node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true
+ },
+ "node_modules/grunt-legacy-util/node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/grunt/node_modules/grunt-cli": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.4.3.tgz",
+ "integrity": "sha512-9Dtx/AhVeB4LYzsViCjUQkd0Kw0McN2gYpdmGYKtE2a5Yt7v1Q+HYZVWhqXc/kGnxlMtqKDxSwotiGeFmkrCoQ==",
+ "dev": true,
+ "dependencies": {
+ "grunt-known-options": "~2.0.0",
+ "interpret": "~1.1.0",
+ "liftup": "~3.0.1",
+ "nopt": "~4.0.1",
+ "v8flags": "~3.2.0"
+ },
+ "bin": {
+ "grunt": "bin/grunt"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/grunt/node_modules/grunt-cli/node_modules/nopt": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz",
+ "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==",
+ "dev": true,
+ "dependencies": {
+ "abbrev": "1",
+ "osenv": "^0.1.4"
+ },
+ "bin": {
+ "nopt": "bin/nopt.js"
+ }
+ },
+ "node_modules/gzip-size": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz",
+ "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==",
+ "dev": true,
+ "dependencies": {
+ "duplexer": "^0.1.1",
+ "pify": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/handlebars": {
+ "version": "4.7.7",
+ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz",
+ "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==",
+ "dependencies": {
+ "minimist": "^1.2.5",
+ "neo-async": "^2.6.0",
+ "source-map": "^0.6.1",
+ "wordwrap": "^1.0.0"
+ },
+ "bin": {
+ "handlebars": "bin/handlebars"
+ },
+ "engines": {
+ "node": ">=0.4.7"
+ },
+ "optionalDependencies": {
+ "uglify-js": "^3.1.4"
+ }
+ },
+ "node_modules/has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dev": true,
+ "dependencies": {
+ "function-bind": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/has-ansi": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+ "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/homedir-polyfill": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz",
+ "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==",
+ "dev": true,
+ "dependencies": {
+ "parse-passwd": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/hooker": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz",
+ "integrity": "sha512-t+UerCsQviSymAInD01Pw+Dn/usmz1sRO+3Zk1+lx8eg+WKpD2ulcwWqHHL0+aseRBr+3+vIhiG1K1JTwaIcTA==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/htmlparser2": {
+ "version": "3.8.3",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz",
+ "integrity": "sha512-hBxEg3CYXe+rPIua8ETe7tmG3XDn9B0edOE/e9wH2nLczxzgdu0m0aNHY+5wFZiviLWLdANPJTssa92dMcXQ5Q==",
+ "dev": true,
+ "dependencies": {
+ "domelementtype": "1",
+ "domhandler": "2.3",
+ "domutils": "1.5",
+ "entities": "1.0",
+ "readable-stream": "1.1"
+ }
+ },
+ "node_modules/http-parser-js": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz",
+ "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==",
+ "dev": true
+ },
+ "node_modules/https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "dev": true,
+ "dependencies": {
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "dev": true,
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dev": true,
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "node_modules/ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true
+ },
+ "node_modules/interpret": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz",
+ "integrity": "sha512-CLM8SNMDu7C5psFCn6Wg/tgpj/bKAg7hc2gWqcuR9OD5Ft9PhBpIu8PLicPeis+xDd6YX2ncI8MCA64I9tftIA==",
+ "dev": true
+ },
+ "node_modules/is-absolute": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz",
+ "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==",
+ "dev": true,
+ "dependencies": {
+ "is-relative": "^1.0.0",
+ "is-windows": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+ "dev": true
+ },
+ "node_modules/is-core-module": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
+ "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
+ "dev": true,
+ "dependencies": {
+ "has": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "dependencies": {
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-relative": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz",
+ "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==",
+ "dev": true,
+ "dependencies": {
+ "is-unc-path": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-unc-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
+ "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==",
+ "dev": true,
+ "dependencies": {
+ "unc-path-regex": "^0.1.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-windows": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+ "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==",
+ "dev": true
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "node_modules/isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/jquery": {
+ "version": "3.6.3",
+ "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.3.tgz",
+ "integrity": "sha512-bZ5Sy3YzKo9Fyc8wH2iIQK4JImJ6R0GWI9kL1/k7Z91ZBNgkRXE6U0JfHIizZbort8ZunhSI3jw9I6253ahKfg=="
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
+ "node_modules/js-yaml": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+ "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jshint": {
+ "version": "2.13.6",
+ "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.13.6.tgz",
+ "integrity": "sha512-IVdB4G0NTTeQZrBoM8C5JFVLjV2KtZ9APgybDA1MK73xb09qFs0jCXyQLnCOp1cSZZZbvhq/6mfXHUTaDkffuQ==",
+ "dev": true,
+ "dependencies": {
+ "cli": "~1.0.0",
+ "console-browserify": "1.1.x",
+ "exit": "0.1.x",
+ "htmlparser2": "3.8.x",
+ "lodash": "~4.17.21",
+ "minimatch": "~3.0.2",
+ "strip-json-comments": "1.0.x"
+ },
+ "bin": {
+ "jshint": "bin/jshint"
+ }
+ },
+ "node_modules/jshint/node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true
+ },
+ "node_modules/json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+ "dev": true
+ },
+ "node_modules/kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/liftup": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/liftup/-/liftup-3.0.1.tgz",
+ "integrity": "sha512-yRHaiQDizWSzoXk3APcA71eOI/UuhEkNN9DiW2Tt44mhYzX4joFoCZlxsSOF7RyeLlfqzFLQI1ngFq3ggMPhOw==",
+ "dev": true,
+ "dependencies": {
+ "extend": "^3.0.2",
+ "findup-sync": "^4.0.0",
+ "fined": "^1.2.0",
+ "flagged-respawn": "^1.0.1",
+ "is-plain-object": "^2.0.4",
+ "object.map": "^1.0.1",
+ "rechoir": "^0.7.0",
+ "resolve": "^1.19.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/liftup/node_modules/findup-sync": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-4.0.0.tgz",
+ "integrity": "sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ==",
+ "dev": true,
+ "dependencies": {
+ "detect-file": "^1.0.0",
+ "is-glob": "^4.0.0",
+ "micromatch": "^4.0.2",
+ "resolve-dir": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "dev": true
+ },
+ "node_modules/livereload-js": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-2.4.0.tgz",
+ "integrity": "sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw==",
+ "dev": true
+ },
+ "node_modules/lodash": {
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
+ "dev": true
+ },
+ "node_modules/make-iterator": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz",
+ "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^6.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/map-cache": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
+ "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/maxmin": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/maxmin/-/maxmin-3.0.0.tgz",
+ "integrity": "sha512-wcahMInmGtg/7c6a75fr21Ch/Ks1Tb+Jtoan5Ft4bAI0ZvJqyOw8kkM7e7p8hDSzY805vmxwHT50KcjGwKyJ0g==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.1.0",
+ "figures": "^3.2.0",
+ "gzip-size": "^5.1.1",
+ "pretty-bytes": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+ "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+ "dev": true,
+ "dependencies": {
+ "braces": "^3.0.2",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
+ },
+ "node_modules/mkdirp-classic": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
+ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
+ "dev": true
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "node_modules/neo-async": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
+ },
+ "node_modules/nice-try": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+ "dev": true
+ },
+ "node_modules/node-fetch": {
+ "version": "2.6.7",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
+ "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
+ "dev": true,
+ "dependencies": {
+ "whatwg-url": "^5.0.0"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ },
+ "peerDependencies": {
+ "encoding": "^0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "encoding": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/nopt": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
+ "integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==",
+ "dev": true,
+ "dependencies": {
+ "abbrev": "1"
+ },
+ "bin": {
+ "nopt": "bin/nopt.js"
+ }
+ },
+ "node_modules/nsdeclare": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/nsdeclare/-/nsdeclare-0.1.0.tgz",
+ "integrity": "sha512-Wb+BpXFfacpp1cgrQoO5Q2wKHACuMlUE6uayGFFLF3yVuXejBp5Rflk991hWvTQbUuQslXTIvBNOHqYTvzBSjA==",
+ "dev": true
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object.defaults": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz",
+ "integrity": "sha512-c/K0mw/F11k4dEUBMW8naXUuBuhxRCfG7W+yFy8EcijU/rSmazOUd1XAEEe6bC0OuXY4HUKjTJv7xbxIMqdxrA==",
+ "dev": true,
+ "dependencies": {
+ "array-each": "^1.0.1",
+ "array-slice": "^1.0.0",
+ "for-own": "^1.0.0",
+ "isobject": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object.map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz",
+ "integrity": "sha512-3+mAJu2PLfnSVGHwIWubpOFLscJANBKuB/6A4CxBstc4aqwQY0FWcsppuy4jU5GSB95yES5JHSI+33AWuS4k6w==",
+ "dev": true,
+ "dependencies": {
+ "for-own": "^1.0.0",
+ "make-iterator": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object.pick": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
+ "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==",
+ "dev": true,
+ "dependencies": {
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/os-homedir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
+ "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/osenv": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
+ "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
+ "deprecated": "This package is no longer supported.",
+ "dev": true,
+ "dependencies": {
+ "os-homedir": "^1.0.0",
+ "os-tmpdir": "^1.0.0"
+ }
+ },
+ "node_modules/p-each-series": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz",
+ "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parse-filepath": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz",
+ "integrity": "sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q==",
+ "dev": true,
+ "dependencies": {
+ "is-absolute": "^1.0.0",
+ "map-cache": "^0.2.0",
+ "path-root": "^0.1.1"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/parse-json": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parse-passwd": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
+ "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+ "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
+ },
+ "node_modules/path-root": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz",
+ "integrity": "sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==",
+ "dev": true,
+ "dependencies": {
+ "path-root-regex": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-root-regex": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz",
+ "integrity": "sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pend": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
+ "dev": true
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pify": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/pretty-bytes": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
+ "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/progress": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+ "dev": true
+ },
+ "node_modules/pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dev": true,
+ "dependencies": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "node_modules/puppeteer": {
+ "version": "19.7.1",
+ "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-19.7.1.tgz",
+ "integrity": "sha512-Hampj7jHlicySL1sSLHCwoFoRCi6RcEbnZmRE5brtbk0mp6Td33+9kWQD2eFs09772JIt00ybPKr50Gt7Y18Xg==",
+ "deprecated": "< 22.8.2 is no longer supported",
+ "dev": true,
+ "hasInstallScript": true,
+ "dependencies": {
+ "cosmiconfig": "8.0.0",
+ "https-proxy-agent": "5.0.1",
+ "progress": "2.0.3",
+ "proxy-from-env": "1.1.0",
+ "puppeteer-core": "19.7.1"
+ },
+ "engines": {
+ "node": ">=14.1.0"
+ }
+ },
+ "node_modules/puppeteer-core": {
+ "version": "19.7.1",
+ "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-19.7.1.tgz",
+ "integrity": "sha512-4b5Go25IA+0xrUIw0Qtqi4nxc0qwdu/C7VT1+tFPl1W27207YT+7bxfANC3PjXMlS6bcbzinCf5YfGqMl8tfyQ==",
+ "dev": true,
+ "dependencies": {
+ "cross-fetch": "3.1.5",
+ "debug": "4.3.4",
+ "devtools-protocol": "0.0.1094867",
+ "extract-zip": "2.0.1",
+ "https-proxy-agent": "5.0.1",
+ "proxy-from-env": "1.1.0",
+ "rimraf": "3.0.2",
+ "tar-fs": "2.1.1",
+ "unbzip2-stream": "1.4.3",
+ "ws": "8.11.0"
+ },
+ "engines": {
+ "node": ">=14.1.0"
+ },
+ "peerDependencies": {
+ "chromium-bidi": "0.4.3",
+ "typescript": ">= 4.7.4"
+ },
+ "peerDependenciesMeta": {
+ "chromium-bidi": {
+ "optional": true
+ },
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/puppeteer-core/node_modules/rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "deprecated": "Rimraf versions prior to v4 are no longer supported",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/qs": {
+ "version": "6.9.4",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz",
+ "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-1.1.7.tgz",
+ "integrity": "sha1-HQJ8K/oRasxmI7yo8AAWVyqH1CU=",
+ "dev": true,
+ "dependencies": {
+ "bytes": "1",
+ "string_decoder": "0.10"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/readable-stream": {
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+ "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==",
+ "dev": true,
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "node_modules/rechoir": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz",
+ "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==",
+ "dev": true,
+ "dependencies": {
+ "resolve": "^1.9.0"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/resolve": {
+ "version": "1.22.1",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
+ "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
+ "dev": true,
+ "dependencies": {
+ "is-core-module": "^2.9.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-dir": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz",
+ "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==",
+ "dev": true,
+ "dependencies": {
+ "expand-tilde": "^2.0.0",
+ "global-modules": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "deprecated": "Rimraf versions prior to v4 are no longer supported",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/safe-json-parse": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/safe-json-parse/-/safe-json-parse-1.0.1.tgz",
+ "integrity": "sha1-PnZyPjjf3aE8mx0poeB//uSzC1c=",
+ "dev": true
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true
+ },
+ "node_modules/semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+ "dev": true,
+ "dependencies": {
+ "shebang-regex": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/sprintf-js": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
+ "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==",
+ "dev": true
+ },
+ "node_modules/string_decoder": {
+ "version": "0.10.31",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+ "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
+ "dev": true
+ },
+ "node_modules/string-template": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz",
+ "integrity": "sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0=",
+ "dev": true
+ },
+ "node_modules/strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz",
+ "integrity": "sha512-AOPG8EBc5wAikaG1/7uFCNFJwnKOuQwFTpYBdTW6OvWHeZBQBrAA/amefHGrEiOnCPcLFZK6FUPtWVKpQVIRgg==",
+ "dev": true,
+ "bin": {
+ "strip-json-comments": "cli.js"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/tar-fs": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
+ "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==",
+ "dev": true,
+ "dependencies": {
+ "chownr": "^1.1.1",
+ "mkdirp-classic": "^0.5.2",
+ "pump": "^3.0.0",
+ "tar-stream": "^2.1.4"
+ }
+ },
+ "node_modules/tar-stream": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
+ "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
+ "dev": true,
+ "dependencies": {
+ "bl": "^4.0.3",
+ "end-of-stream": "^1.4.1",
+ "fs-constants": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^3.1.1"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/tar-stream/node_modules/readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "dev": true,
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/tar-stream/node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/through": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
+ "dev": true
+ },
+ "node_modules/tiny-lr": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-1.1.1.tgz",
+ "integrity": "sha512-44yhA3tsaRoMOjQQ+5v5mVdqef+kH6Qze9jTpqtVufgYjYt08zyZAwNwwVBj3i1rJMnR52IxOW0LK0vBzgAkuA==",
+ "dev": true,
+ "dependencies": {
+ "body": "^5.1.0",
+ "debug": "^3.1.0",
+ "faye-websocket": "~0.10.0",
+ "livereload-js": "^2.3.0",
+ "object-assign": "^4.1.0",
+ "qs": "^6.4.0"
+ }
+ },
+ "node_modules/tiny-lr/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/tiny-lr/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+ "dev": true
+ },
+ "node_modules/uglify-js": {
+ "version": "3.12.4",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.12.4.tgz",
+ "integrity": "sha512-L5i5jg/SHkEqzN18gQMTWsZk3KelRsfD1wUVNqtq0kzqWQqcJjyL8yc1o8hJgRrWqrAl2mUFbhfznEIoi7zi2A==",
+ "optional": true,
+ "bin": {
+ "uglifyjs": "bin/uglifyjs"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/unbzip2-stream": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz",
+ "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==",
+ "dev": true,
+ "dependencies": {
+ "buffer": "^5.2.1",
+ "through": "^2.3.8"
+ }
+ },
+ "node_modules/unc-path-regex": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
+ "integrity": "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/underscore.string": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.6.tgz",
+ "integrity": "sha512-VoC83HWXmCrF6rgkyxS9GHv8W9Q5nhMKho+OadDJGzL2oDYbYEppBaCMH6pFlwLeqj2QS+hhkw2kpXkSdD1JxQ==",
+ "dev": true,
+ "dependencies": {
+ "sprintf-js": "^1.1.1",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/uri-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/uri-path/-/uri-path-1.0.0.tgz",
+ "integrity": "sha512-8pMuAn4KacYdGMkFaoQARicp4HSw24/DHOVKWqVRJ8LhhAwPPFpdGvdL9184JVmUwe7vz7Z9n6IqI6t5n2ELdg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "dev": true
+ },
+ "node_modules/v8flags": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz",
+ "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==",
+ "dev": true,
+ "dependencies": {
+ "homedir-polyfill": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
+ "dev": true
+ },
+ "node_modules/websocket-driver": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
+ "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==",
+ "dev": true,
+ "dependencies": {
+ "http-parser-js": ">=0.5.1",
+ "safe-buffer": ">=5.1.0",
+ "websocket-extensions": ">=0.1.1"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/websocket-extensions": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
+ "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "dev": true,
+ "dependencies": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
+ "node_modules/which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "which": "bin/which"
+ }
+ },
+ "node_modules/wordwrap": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+ "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "node_modules/ws": {
+ "version": "8.11.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
+ "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
+ "dev": true,
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": "^5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/yauzl": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
+ "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
+ "dev": true,
+ "dependencies": {
+ "buffer-crc32": "~0.2.3",
+ "fd-slicer": "~1.1.0"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
index 748cbbf..93fcd3f 100644
--- a/package.json
+++ b/package.json
@@ -1,39 +1,52 @@
{
"name": "jquery-storelocator-plugin",
- "version": "2.0.5",
- "description": "This jQuery plugin takes advantage of Google Maps API version 3 to create an easy to implement store locator. No back-end programming is required, you just need to feed it KML, XML, or JSON data with all the location information.",
- "repository": {
- "type": "git",
- "url": "https://github.com/bjorn2404/jQuery-Store-Locator-Plugin.git"
- },
- "keywords": ["jquery-plugin","locator","store", "location","locations","maps","map","stores","find"],
- "author": {
- "name": "Bjorn Holine",
- "url": "http://www.bjornblog.com/"
- },
- "licenses": [
- {
- "type": "MIT",
- "url": "https://github.com/bjorn2404/jQuery-Store-Locator-Plugin/blob/master/license.txt"
- }
- ],
- "bugs": {
- "url": "https://github.com/bjorn2404/jQuery-Store-Locator-Plugin/issues"
- },
- "dependencies": {
- "jquery": ">=1.7",
- "handlebars": ">=1.0"
- },
+ "version": "3.4.1",
+ "description": "This jQuery plugin takes advantage of Google Maps API version 3 to create an easy to implement store locator. No back-end programming is required, you just need to feed it KML, XML, or JSON data with all the location information.",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/bjorn2404/jQuery-Store-Locator-Plugin.git"
+ },
+ "keywords": [
+ "jquery-plugin",
+ "ecosystem:jquery",
+ "locator",
+ "dealer",
+ "store",
+ "location",
+ "locations",
+ "maps",
+ "map",
+ "stores",
+ "find",
+ "finder"
+ ],
+ "author": {
+ "name": "Bjorn Holine",
+ "url": "https://www.bjornblog.com/"
+ },
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/bjorn2404/jQuery-Store-Locator-Plugin/issues"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "dependencies": {
+ "handlebars": ">=4.7.7",
+ "jquery": "^3.6.3"
+ },
"devDependencies": {
- "grunt-contrib-jshint": "~0.6.0",
- "grunt-contrib-qunit": "~0.2.0",
- "grunt-contrib-concat": "~0.3.0",
- "grunt-contrib-uglify": "~0.2.0",
- "grunt-contrib-watch": "~0.4.0",
- "grunt-contrib-clean": "~0.4.0",
- "grunt-contrib-less": "~0.11.0",
- "grunt": "~0.4.4",
- "grunt-banner": "~0.2.2",
- "grunt-contrib-cssmin": "~0.9.0"
+ "grunt": "~1.6.1",
+ "grunt-banner": "~0.6.0",
+ "grunt-contrib-clean": "^2.0.1",
+ "grunt-contrib-concat": "~2.1.0",
+ "grunt-contrib-copy": "^1.0.0",
+ "grunt-contrib-cssmin": "~4.0.0",
+ "grunt-contrib-handlebars": "^3.0.0",
+ "grunt-contrib-jshint": "~3.2.0",
+ "grunt-contrib-qunit": "~7.0.0",
+ "grunt-contrib-sass": "^2.0.0",
+ "grunt-contrib-uglify": "~5.2.2",
+ "grunt-contrib-watch": "~1.1.0"
}
}
diff --git a/readme.md b/readme.md
index aeb269e..aa5d600 100644
--- a/readme.md
+++ b/readme.md
@@ -25,17 +25,384 @@ Mustache templates, and the built-in helpers really come in handy. Depending on
want them in the default location. If you’re developing something for mobile devices the templates can be pre-compiled
for even faster loading.
+### WordPress version
+
+[Cardinal Locator - WordPress store locator plugin](https://cardinalwp.com/) is now available, which uses this jQuery plugin
+as a base and all of the settings can be set via a settings page in the WP dashboard. It also integrates with core
+WordPress features such as custom post types for location data and custom taxonomies for location categorization and
+filtering.
+
+
## Changelog
+### Version 3.4.1
+
+* Fixed default directions link (inline directions disabled). Props [@HarldVerbiesenVIPMarketingBV](https://github.com/HarldVerbiesenVIPMarketingBV) via [issue #304](https://github.com/bjorn2404/jQuery-Store-Locator-Plugin/issues/304)
+
+### Version 3.4.0
+
+* Added support for new Map ID with mapSettingsID setting. Please refer to [Use Map IDs](https://developers.google.com/maps/documentation/get-map-id) and [Map ID with Styling](https://developers.google.com/maps/documentation/javascript/examples/map-id-style) in the Google API docs.
+* Added support for new [Advanced Markers](https://developers.google.com/maps/documentation/javascript/advanced-markers/overview) while maintaining support for the deprecated (not yet discontinued) marker functionality. mapSettingsID setting needs to be set and marker library needs to be included - see previous item.
+* Fixed bugs with pagination.
+* Fixed new "Google Maps JavaScript API has been loaded directly without loading=async" notice.
+* Improved pagination accessibility and functionality.
+* Updated jQuery in example files to v3.7.1
+
+### Version 3.3.0
+
+* Updated max distance functionality to make distance changes apply dynamically vs. having to manually click button.
+
+### Version 3.2.1
+
+* Swapped old maps.google.com API domain to maps.googleapis.com in all example files.
+
+### Version 3.2.0
+
+* Added new Google Maps lazy load setting and example file - see new lazyLoadMap and apiKey settings.
+
+### Version 3.1.14
+
+* Added label tags to radio button markup in categories example file.
+* Fix - reverted removal of zoom reset to 0 after taxonomy filtering due to introduction of new issue.
+* Fixed comment typos.
+
+### Version 3.1.13
+
+* Fixed additional disable filtering functionality related to select options and radio buttons by globally tracking the disabled values.
+* Removed zoom reset to zero on taxonomy filtering to keep searched location in view.
+* Updated maybeDisableFilterOptions to run when full map start or default location settings are enabled.
+
+### Version 3.1.12
+
+* Added automatic reset functionality that fires when address input field value is removed (changed to blank).
+* Fixed additional issues with new disable filtering functionality with select fields, radio buttons, and updated address input value.
+* Fixed markerClusterer library usage of deprecated Google Maps addDomListener. Props [@marcohanke](https://github.com/marcohanke) via [#294](https://github.com/bjorn2404/jQuery-Store-Locator-Plugin/pull/294/)
+* Updated jQuery version in example files.
+* Updated query string functionality to fill in address and name search with query string values in search form.
+
+### Version 3.1.11
+
+* Fixed issue with new disable filtering functionality with radio buttons.
+* Fixed issue with reset button where all locations were duplicated - introduced in v3.0.1. Reported in https://github.com/bjorn2404/jQuery-Store-Locator-Plugin/issues/293.
+* Updated functionality to reset disabled form filters when Reset button is clicked.
+
+### Version 3.1.10
+
+* Added map marker accessibility.
+* Deprecated bounceMarker setting due to Google Charts API deprecation and poor animation results with Google marker labels.
+* Fixed issue with new disable filtering functionality when location objet property is missing.
+* Fixed marker labels not working with previous technique. Swapped to google.maps.Marker label parameter.
+* Updated package devDependencies.
+
+### Version 3.1.9
+
+* Added functionality to disable input and option fields that don't have matching values in the current location set after filtering when inclusive filtering is used (default).
+* Extended nameAttribute settings so multiple attributes can be searched. Separate attribute names with commas.
+* Fixed location set length scenario when fullMapStart is enabled and taxFilters is reset and name search and origin are empty.
+* Show empty result message if no locations with default sorting.
+* Temporary fix for missing Google Maps callback parameter console error - the parameter requirement was not previously enforced on the API side.
+* Updated deprecated google.maps.event.addDomListener usage with window.addEventListener.
+
+### Version 3.1.8
+
+* Added coordinate range check to exclude locations with invalid latitude and longitude values.
+
+### Version 3.1.7
+
+* Fixed empty name search value not clearing previous value.
+* Fixed openNearest setting not working in combination with querystringParams setting.
+* Fixed openNearest setting not opening correct location with custom sorting (sortBy) enabled.
+
+### Version 3.1.6
+
+* Added extra check to make sure mapping doesn't fire twice when defaultLoc is enabled and fullMapStart is also enabled.
+ fullMapStart is not needed when defaultLoc is enabled and this is just a preventative to avoid user errors.
+* Fixed centering issue on initial search with maxDistance enabled introduced with fitBounds updates in last update.
+
+### Version 3.1.5
+
+* Added Google Maps object as a parameter for [callbackMarkerClick](callbacks/callback-markerclick.md) callback.
+* Added a zoom listener after fitBounds is used to prevent high zoom levels after name search and taxonomy filtering.
+* Improved zooming when maxDistance setting is enabled taking advantage of the fitBounds method.
+
+### Version 3.1.4
+
+* Fixed name search filter value not clearing if form input is cleared.
+
+### Version 3.1.3
+
+* Fixed empty name search field value overriding filter results.
+* Fixed groups of filters not applying together.
+* Fixed potential error from occurring if fullMapStartListLimit is set, and the number of locations is less than the limit.
+* Updated bundled Handlebars to v4.7.7 that addresses a [critical vulnerability](https://github.com/advisories/GHSA-f2jv-r9rf-7988).
+* Updated jQuery version in example files to v3.6.0.
+
+### Version 3.1.2
+
+* Fixed name search issue introduced in v3.1.1. Reverted to previous matching pattern only for name searches and still using the new patter for taxonomy matching.
+
+### Version 3.1.1
+
+* Enhanced filtering regular expression to better account for exact matches vs. substrings.
+* Fixed multi-category selection filtering issue introduced in last version 3.1.0.
+* Updated bundled Handlebars to v4.7.6.
+* Updated node modules.
+
+### Version 3.1.0
+
+* Added featuredDistance setting to restrict featured locations by a specified distance (number value should be used).
+* Updated bundled version of Handlebars to v4.5.1 due to security issue.
+* Updated taxonomy filtering REGEX and string replacements for international character support.
+
+### Version 3.0.1
+
+* Added custom order handling to tie into previously added custom sorting. Set order to asc or desc.
+* Added functionality to fill in search input with determined address when using autoGeocode or geocodeID settings.
+* Added Google Map object as parameter to callbackBeforeSend, callbackListClick, callbackModalReady, and callbackFilters callbacks.
+* Changed parseJSON to native JSON.parse due to deprecation in rawData processing function.
+* Fixed issue with mapReload (triggered with optional reset button) where storeLimit wasn't reset.
+* Updated sort-example.html example with order select field.
+
+### Version 3.0.0
+
+Version 3 has a breaking change with the dataLocation and dataType settings switching the default from XML to JSON.
+
+* Added ajaxData to allow custom data to be sent with the AJAX request. The setting accepts an object.
+* Added altDistanceNoResult setting to display no results message vs. all locations when closest location is further than distanceAlert setting.
+* Added callbackAutoGeoSuccess callback that fires after the geolocation API returns a successful result.
+* Added callbackFormVals callback that fires after the form values have been processed from the form.
+* Added callbackGeocodeRestrictions callback that allows the componentRestrictions object to be overridden.
+* Added callbackNearestLoc callback that fires when the nearest location is triggered with the openNearest setting.
+* Added callbackSorting callback that fires when when a new sorting method is selected.
+* Added component filtering for geocoding to better restrict by area.
+* Added length unit (distance miles/kilometers) front-end swap functionality, setting and example file.
+* Added mappingObject, originPoint, data, and page parameters to callbackSuccess callback.
+* Added new front-end sorting functionality, settings, and example file.
+* Added location data object as parameter of callbackDirectionsRequest callback.
+* Added openNearest setting to open/select the nearest location after searching.
+* Fixed issue with featuredLocations setting where featured locations at far distances would trigger distance alert.
+* Fixed issue with filtering values containing ampersands, which would not display any results - updated filtering regex.
+* Fixed issue where HTML5 Geolocation was skipped when using the fullMapStartBlank setting.
+* Fixed issue with pagination page numbers displaying after no results via PR from [heldr88](https://github.com/heldr88)
+* Fixed issue with taxonomy filtering and autoGeocode setting where HTML Geocoding API would run on every filter change.
+* Removed Less from the project as it is no longer needed with the Bootstrap update.
+* Swapped the default location data type to be JSON instead of XML.
+* Updated Bootstrap example file to make use of Bootstrap 4.
+* Updated taxonomy filtering so pagination is reset to first page after selecting a filter.
+
+### Version 2.7.4
+
+* Fixed error when filtering with query strings where filter values with spaces wouldn't work.
+* Updated processForm method so submitting the map removes focus from any of the form input/select fields instead of just the address input.
+* Updated filterData string replace methods to match string replace method in filters setup.
+
+### Version 2.7.3
+
+* Added ability to indicate multiple query string parameter values (for checkboxes) with a comma separated list value.
+* Added autoCompleteDisableListener setting to disable the listener that immediately triggers a search when an auto complete location option is selected.
+* Added blur to primary location input field after form submission to hide mobile keyboards.
+* Added check to exclusive filtering to make sure filter values are not undefined before proceeding with the regular expression.
+* Added functionality to automatically select/check filters on load from query string parameter values.
+* Added location details object to callbackListClick and callbackMarkerClick objects.
+* Fixed broken dragSearch functionality that was introduced after map scope pull request was merged.
+* Fixed Handlebars targeting issue triggered by placing an unordered list within the location list template.
+* Fixed issue with fullMapStart where conditional was checking if isNaN was true when it should have been false on fullMapStartListLimit setting.
+* Updated callbackListClick documentation to include second market object parameter.
+* Updated zooming to prevent fitBounds from being used when query string parameters are in use and the location has been set with bh-sl-address.
+
+### Version 2.7.2
+
+* Added [callbackRegion](callbacks/callback-region.md) callback, which allows region to be set before being sent to the Google Maps Geocoding API.
+* Fixed incorrect origin marker parameter order after code restructure.
+* Fixed [issue](https://github.com/bjorn2404/jQuery-Store-Locator-Plugin/issues/160) where searching by name after searching by address, without a new address, didn't reset the origin.
+* Merged pull-request from [ollea](https://github.com/ollea) that adds "getMap" function that returns a google.maps.Map instance.
+
+### Version 2.7.1
+
+* Hotfix to prevent potential error with updated filterData method if the category of a location is undefined.
+
+### Version 2.7.0
+
+* Added [callback documentation](callbacks.md).
+* Added callbackCreateMarker for custom marker overrides.
+* Added [InfoBubble](https://github.com/googlemaps/js-info-bubble) support and example file.
+* Added location results total count if HTML element with "bh-sl-total-results" class exists.
+* Added checks to replace non-ASCII characters when filtering.
+* Added reset functionality that can be triggered via a button that has the CSS class "bh-sl-reset".
+* Added query string parameter filter check so that results can be filtered with URL query strings.
+* Fixed issue with maxDistance and querystringParams settings combination.
+* Moved some functionality from processData into new separate methods.
+* Removed non-standard $1 RegExp property in processData method.
+
+### Version 2.6.2
+
+* Added callbackMapSet callback that fires after the map has been set up.
+* Fixed issue where locations without attributes set could get the attribute values from prior locations.
+* Fixed issue where pagination total number of pages was based on the full location set total instead of the storeLimit setting.
+* Removed form markup from initial query string example index file as it's not needed until the submission page.
+
+### Version 2.6.1
+
+* Added additional error handling when the plugin checks the closest location.
+* Added listener for autoComplete change so that the search processes when a new place is selected.
+* Fixed [issue with new boundary search](https://github.com/bjorn2404/jQuery-Store-Locator-Plugin/issues/127) AJAX params after a full address search was made.
+* Merged in pull request from [noclat](https://github.com/noclat) that added autoCompleteOptions setting.
+
+### Version 2.6.0
+
+* Added bounds and formatted address info from geocoding API to AJAX data parameters.
+* Added Marker Clusterer library support, setting and example file.
+* Added disableAlphaMarkers setting to completely disable displaying markers and location list indicators with alpha characters.
+* Fixed issue with combination of autoGeocode and originMarker settings.
+* Merged in pull request from [drcomix](https://github.com/drcomix) that fixed zoom issue with dragSearch setting.
+* Switched included remote scripts in example files to https.
+* Updated jQuery references to the latest version.
+
+### Version 2.5.3
+
+* Removed check from createMarker method that removed spaces from categories - created issues with categories that have spaces.
+* Re-worked handling of no results.
+* Updated createMarker method to ensure custom category marker images are converted to integers if strings are passed for dimensions.
+* Updated autoGeocode and default location functionality so that max distance is applied on initial load.
+
+### Version 2.5.2
+
+* Fixed pagination bubbling issue.
+* Fixed pagination issues with autoGeocode and dragSearch combinations.
+
+### Version 2.5.1
+
+* Fixed issues with visibleMarkersList and location list background colors and selection.
+
+### Version 2.5.0
+
+* Added new dragSearch setting which performs a new search when the map is dragged when enabled.
+* Added new geocodeID setting so that the HTML geocoding API can be triggered by a button instead of firing automatically.
+* Fixed issues with no results where clicking the marker would display data from the previous result and clicking the location list item would throw an error.
+* Merged in pull request from [hawkmeister](https://github.com/hawkmeister) with update to bower.json file with main property.
+* Merged in pull request from [hyperTwitch](https://github.com/hyperTwitch) with fixes for using fullMapStartListLimit in combination with a different store limit.
+* Updated jQuery references to the latest version.
+
+### Version 2.4.2
+
+* Fixed issue with new full map start location list limit where clicking on a marker that didn't have a list item
+displayed caused an error.
+* Fixed issue with settings combination of inline directions and default location.
+* Reverted change to new list limit so that it's always applied with full map start enabled.
+
+### Version 2.4.1
+
+* Changed new full map start list limit so that it's only applied on the initial load.
+* Fixed issue with new autocomplete setting where search was firing twice.
+
+### Version 2.4.0
+
+* Added new selected marker image options to highlight clicked marker.
+* Added Google Places autocomplete option and example file.
+* Added full map start location list limit setting.
+
+### Version 2.3.3
+
+* Removed code that temporarily hid the map and results, when there are no results, in favor of just displaying the no
+results message and empty map.
+
+### Version 2.3.2
+
+* Tweaked list label width styling.
+
+### Version 2.3.1
+
+* Added preventative styling to inline directions panel table.
+* Switched to unitless line-heights.
+
+### Version 2.3.0
+
+* Added fullMapStartBlank option to show a blank map without any locations initially. Set this setting to an integer,
+which will be applied as the initial Google Maps zoom value and will then fall back to the mapSettings zoom level after
+a search is performed.
+* Added fullMapStartBlank example file.
+* Fixed filters select field styling inconsistency.
+* Moved pagination container within map container div in pagination example to avoid confusion when combined with modal option.
+* Reworked styling so that all HTML example files are responsive by default.
+* Updated map-container ID in all example files with bh-sl prefix.
+
+### Version 2.2.2
+
+* Added preventative styling to avoid table conflicts with directions panel.
+* Fixed clearMarkers issue with inline directions enabled.
+
+### Version 2.2.1
+
+* Updated preventative styling to be more specific to the map container and added max-height img rule.
+
+### Version 2.2.0
+
+* Added check for Google Maps API.
+* Added Grunt Handlebars task for compiling Handlebars templates from src directory - will add more compatibility in future release.
+* Added preventative styling to avoid conflicts with CSS frameworks and resets.
+* Default design refresh.
+* Fixed bug with inline directions panel that occurred after multiple address submissions.
+* Removed sensor parameter from Google Maps API URL as it's no longer needed.
+* Switched the default plugin styling from LESS to SASS.
+* Updated included Handlebars to v4.0.5.
+
+### Version 2.1.0
+
+Includes contributions from from [Giancarlo Gomez](https://github.com/GiancarloGomez).
+
+* Added ability to pass in array object as dataRaw.
+* Added writeDebug console.log helper function for debugging.
+* Added sessionStorage option to store user's location when autoGeocode in enabled to prevent multiple lookups per session.
+* Fixed bug with inline directions panel that occurred after multiple address submissions.
+* Updated processForm method form field variables with empty string fallback values.
+
+### Version 2.0.9
+
+* Fixed issue when using catMarkers setting and not setting a location's category resulted in an error.
+
+### Version 2.0.8
+
+* Changed infowindow and location list templates so that the comma is added if the city is available.
+* Fixed issue with inline directions where "null" was prepended to the destination address.
+* Fixed close directions bug where close icon couldn't be clicked more than two times.
+* Fixed bug where form wasn't overriding query string parameters.
+* Updated processForm method to accept max distance query string parameter.
+* Updated processForm method to use existing origin data if it's present and matches to avoid unnecessary geocode
+requests.
+* Updated max distance check to less than or equal to the selected distance vs. just less than.
+* Updated regionID description in options.md for clarity.
+* Updated formEventHandler method to prevent ASP.net form submission on keydown instead of keyup.
+* Updated mapSettings description in options.md to highlight that zoom can be set to 0 for automatic centering and zooming.
+
+### Version 2.0.7
+
+* Fixed bug where reverse geocoding wasn't passing the origin to the templates (autogeocode and default location),
+causing incorrect direction links.
+* Updated location list directions link to use https.
+
+### Version 2.0.6
+
+* Added the option to filter data exclusively rather than inclusively with the exclusiveFiltering setting.
+* Added callbackFilters that fires when a filter is changed and returns the filter values if needed.
+* Added dataRaw option to use raw KML, XML or JSON data instead of the AJAX call.
+* Added basic raw data example rawdata-example.php file.
+* Added visibleMarkersList option that updates the location list to only display data from the markers that are curently
+displayed on the map.
+* Changed the distance error functionality so that the map centers and zooms automatically and all locations are
+displayed on the map.
+* Fixed issue with fullMapStart and inlineDirections setting combination.
+* Fixed issue with global olat and olng variables not being set with autoGeocode setting enabled.
+* Fixed issue with maxDistance and autoGeocode setting combination.
+
### Version 2.0.5
-- Fixed typo with originMarker setup.
-- Made the originMarkerDim setting optional when setting a custom origin marker image - defaults to 32px by 32px.
-- Removed geocodeErrorAlert language option and switched error alerts to custom exceptions so users aren't shown
+* Fixed typo with originMarker setup.
+* Made the originMarkerDim setting optional when setting a custom origin marker image - defaults to 32px by 32px.
+* Removed geocodeErrorAlert language option and switched error alerts to custom exceptions so users aren't shown
multiple alerts.
-- Fixed bug with inline directions where close icon wasn't being removed on page reload.
-- Added callbackListClick that fires when a list element is clicked.
-- Added callbackMarkerClick that fires when a map marker is clicked.
+* Fixed bug with inline directions where close icon wasn't being removed on page reload.
+* Added callbackListClick that fires when a list element is clicked.
+* Added callbackMarkerClick that fires when a map marker is clicked.
### Version 2.0.4
@@ -179,7 +546,7 @@ a duplicate locationset declaration on line 328.
This is a large update that has many updates and improvements. It’s very important to note that the plugin now requires the [Handlebars](http://handlebarsjs.com) template engine. I made this change so that the data that’s displayed in the location list and the infowindows can be easily customized. I also wanted to separate the bulk of the layout additions from the main plugin file. Handlebars pretty slick, will read Mustache templates, and the built-in helpers can really come in handy. Depending on what your data source is, 2 of the 4 total templates will be used (KML vs XML or JSON) and there are options to set the paths of each template if you don’t want them in the default location. If you’re developing something for mobile devices the templates can be pre-compiled for even faster loading. Additionally, I’d also like to note that it probably makes more sense to use KML now as the data source over the other options but I’m definitely leaving XML and JSON support in. XML is still the default datatype but I may switch it to KML in the future.
-####New features:####
+#### New features: ####
**Kilometers option**
This was a no-brainer. You could make the change without too much trouble before but I thought I’d make it easier for the rest of the world.
@@ -254,4 +621,4 @@ Left a couple of console.logs in my code, which was causing IE to hang.
### Version 1.0
-This is my first jQuery plugin and the first time I’ve published anything on Github. Let me know if I can improve something or if I’m making some kind of mistake.
\ No newline at end of file
+This is my first jQuery plugin and the first time I’ve published anything on Github. Let me know if I can improve something or if I’m making some kind of mistake.
diff --git a/src/.jshintrc b/src/.jshintrc
index ea47bc5..2b50bba 100644
--- a/src/.jshintrc
+++ b/src/.jshintrc
@@ -12,10 +12,16 @@
"eqnull": true,
"browser": true,
"devel": false,
- "predef": ["jQuery"],
- "globals": {
- "google": true,
- "document": true,
- "$": true
- }
+ "globals": {
+ "alert": true,
+ "console": true,
+ "define": true,
+ "document": true,
+ "google": true,
+ "Handlebars": true,
+ "InfoBubble": true,
+ "jQuery": true,
+ "MarkerClusterer": true,
+ "window": true
+ }
}
diff --git a/src/css/bootstrap-example.less b/src/css/bootstrap-example.less
deleted file mode 100644
index 56263f7..0000000
--- a/src/css/bootstrap-example.less
+++ /dev/null
@@ -1,249 +0,0 @@
-@white: #fff;
-@gray: #ccc;
-@darkgray: #8e8e8e;
-@textgray: #333;
-@shadow: #656565;
-@darkergray: #2c2c2c;
-@black: #000;
-@orange: #e76737;
-@red: #ae2118;
-@darkred: #961f17;
-@maroon: #451400;
-@arialFont: Arial, Helvetica, sans-serif;
-
-/* Infowindow Roboto font override */
-.gm-style div, .gm-style span, .gm-style label, .gm-style a {
- font-family: @arialFont;
-}
-
-.bh-sl-error {
- clear: both;
- float: left;
- width: 100%;
- padding: 10px 0;
- color: @red;
- font-weight: bold;
-}
-
-.bh-sl-container {
- color: @textgray;
-
- .jumbotron{
- padding-top: 30px;
- }
-
- .form-input {
- input,select,label {
- margin-right: 10px;
- }
- }
-
- .bh-sl-loading {
- float: left;
- margin: 4px 0 0 10px;
- width: 16px;
- height: 16px;
- background: url(../img/ajax-loader.gif) no-repeat;
- }
-
- .bh-sl-filters-container {
- clear: both;
- width: 100%;
- margin: 15px 0;
-
- .bh-sl-filters {
- list-style: none;
- float: left;
- padding: 0;
- margin: 0 100px 0 0;
-
- li {
- display: block;
- clear: left;
- float: left;
- width: 100%;
- margin: 5px 0;
-
- label {
- display: inline;
- }
-
- input {
- display: block;
- float: left;
- margin: 2px 8px 2px 0;
- }
- }
- }
- }
-
- .bh-sl-map-container {
- margin-top: 27px;
-
- a {
- color: @orange;
- text-decoration: none;
- &:hover, &:active {
- text-decoration: underline;
- }
- }
- }
-
- .bh-sl-loc-list {
- height: 530px;
- overflow-x: auto;
-
- ul {
- display: block;
- clear: left;
- float: left;
- width: 100%;
- list-style: none;
- margin: 0;
- padding: 0;
-
- li {
- display: block;
- clear: left;
- float: left;
- margin: 3% 4%;
- cursor: pointer;
- width: 92%;
- border: 1px solid @white; /* Adding this to prevent moving li elements when adding the list-focus class*/
- }
- }
-
- .list-label {
- float: left;
- margin: 10px 0 0 6px;
- padding: 2px 3px;
- width: 10%;
- max-width: 25px;
- text-align: center;
- background: @maroon;
- color: @white;
- font-weight: bold;
- }
-
- .list-details {
- float: left;
- margin-left: 6px;
- width: 80%;
-
- .list-content {
- padding: 10px;
- }
-
- .loc-dist {
- font-weight: bold;
- font-style: italic;
- color: @darkgray;
- }
- }
-
- .list-focus {
- border: 1px solid rgba(150, 31, 23, 0.4);
- -moz-box-shadow: 0 0 8px rgba(150, 31, 23, 0.4);
- -webkit-box-shadow: 0 0 8px rgba(150, 31, 23, 0.4);
- box-shadow: 0 0 8px rgba(150, 31, 23, 0.4);
- transition: border 0.2s linear 0s, box-shadow 0.2s linear 0s;
- }
-
- .bh-sl-close-directions-container {
- width: 100%;
- height: 20px;
- position: relative;
-
- .bh-sl-close-icon {
- top: 0;
- right: 6px;
- }
- }
-
- .bh-sl-noresults-title {
- font-weight: bold;
- color: @red;
- }
- }
-
- .loc-name {
- /* Picked up by both list and infowindows */
- color: @red;
- font-weight: bold;
- }
-
- .bh-sl-map {
- height: 530px;
- }
-
- .bh-sl-pagination-container {
- clear: both;
-
- ol {
- list-style-type: none;
- text-align: center;
- margin: 0;
- padding: 10px 0;
-
- li {
- display: inline-block;
- padding: 10px;
- cursor: pointer;
- font: bold 14px @arialFont;
- color: @red;
- text-decoration: underline;
- }
-
- .bh-sl-current {
- color: @textgray;
- cursor: auto;
- text-decoration: none;
- }
- }
- }
-}
-
-/* Modal window */
-.bh-sl-overlay {
- position: fixed;
- left: 0px;
- top: 0px;
- width: 100%;
- height: 100%;
- z-index: 10000;
- background: url(../img/overlay-bg.png) repeat;
-
- .bh-sl-modal-window {
- position: absolute;
- left: 50%;
- margin-left: -460px; /* width divided by 2 */
- margin-top: 60px;
- width: 920px;
- height: 590px;
- z-index: 10010;
- background: @white;
- border-radius: 10px;
- box-shadow: 0 0 10px #656565;
-
- .bh-sl-modal-content {
- float: left;
- padding: 0 22px; /* there's already a margin on the top of the map-container div */
- }
-
- .bh-sl-close-icon {
- top: -6px;
- right: -6px;
- }
- }
-}
-
-.bh-sl-close-icon {
- position: absolute;
- width: 18px;
- height: 18px;
- cursor: pointer;
- background: @darkergray url(../img/close-icon.png) 3px 3px no-repeat;
- border: 1px solid @black;
- border-radius: 3px;
- box-shadow: 0 0 3px @shadow;
-}
\ No newline at end of file
diff --git a/src/css/bootstrap-example.scss b/src/css/bootstrap-example.scss
new file mode 100644
index 0000000..e6bb36f
--- /dev/null
+++ b/src/css/bootstrap-example.scss
@@ -0,0 +1,299 @@
+$white: #fff;
+$gray: #ccc;
+$darkgray: #8e8e8e;
+$titlegray: #797874;
+$shadow: #656565;
+$textgray: #555;
+$darkergray: #2c2c2c;
+$black: #000;
+$blue: #005293;
+$red: #ae2118;
+$arialFont: Arial, Helvetica, sans-serif;
+
+/* Infowindow Roboto font override */
+.gm-style div, .gm-style span, .gm-style label, .gm-style a {
+ font-family: $arialFont;
+}
+
+.GMAMP-maps-pin-view {
+ color: $black;
+ font: 400 15px/1 $arialFont;
+}
+
+.bh-sl-error {
+ clear: both;
+ float: left;
+ width: 100%;
+ padding: 10px 0;
+ color: $red;
+ font-weight: bold;
+}
+
+.bh-sl-container {
+ color: $textgray;
+
+ /* Avoid image issues with Google Maps and CSS resets */
+ img {
+ max-width: none !important;
+ border-radius: 0 !important;
+ box-shadow: none !important;
+ }
+
+ /* Avoid issues with Google Maps and CSS frameworks */
+ > * {
+ box-sizing: content-box !important;
+ }
+
+ .jumbotron{
+ margin-bottom: 0;
+ }
+
+ .form-input {
+ input,select,label {
+ margin-right: 10px;
+ }
+ }
+
+ .loc-alt-dist {
+ display: none;
+ }
+
+ .bh-sl-loading {
+ float: left;
+ margin: 4px 0 0 10px;
+ width: 16px;
+ height: 16px;
+ background: url(../img/ajax-loader.gif) no-repeat;
+ }
+
+ .bh-sl-filters-container {
+ clear: both;
+ width: 100%;
+ margin: 15px 0;
+
+ .bh-sl-filters {
+ list-style: none;
+ float: left;
+ padding: 0;
+ margin: 0 100px 0 0;
+
+ li {
+ display: block;
+ clear: left;
+ float: left;
+ width: 100%;
+ margin: 5px 0;
+
+ label {
+ display: inline;
+ }
+
+ input {
+ display: block;
+ float: left;
+ margin: 2px 8px 2px 0;
+ }
+ }
+ }
+ }
+
+ .bh-sl-map-container {
+ a {
+ color: $blue;
+ text-decoration: none;
+ &:hover, &:active {
+ text-decoration: underline;
+ }
+ }
+ }
+
+ .bh-sl-loc-list {
+ height: 530px;
+ overflow-x: auto;
+ font-size: 13px;
+
+ ul {
+ display: block;
+ clear: left;
+ float: left;
+ width: 100%;
+ list-style: none;
+ margin: 0;
+ padding: 0;
+
+ li {
+ display: block;
+ clear: left;
+ float: left;
+ margin: 3% 4%;
+ cursor: pointer;
+ width: 92%;
+ border: 1px solid $white; /* Adding this to prevent moving li elements when adding the list-focus class*/
+ }
+ }
+
+ .list-label {
+ float: left;
+ margin: 10px 0 0 6px;
+ padding: 4px;
+ width: 27px;
+ text-align: center;
+ background: darken( $blue, 20% );
+ color: $white;
+ font-weight: bold;
+ border-radius: 15px;
+ }
+
+ .list-details {
+ float: left;
+ margin-left: 6px;
+ width: 80%;
+
+ .list-content {
+ padding: 10px;
+ }
+
+ .loc-dist {
+ font-weight: bold;
+ font-style: italic;
+ color: $darkgray;
+ }
+ }
+
+ .list-focus {
+ border: 1px solid rgba(0, 82, 147, 0.4);
+ -moz-box-shadow: 0 0 8px rgba(0, 82, 147, 0.4);
+ -webkit-box-shadow: 0 0 8px rgba(0, 82, 147, 0.4);
+ box-shadow: 0 0 8px rgba(0, 100, 180, 0.4);
+ transition: border 0.2s linear 0s, box-shadow 0.2s linear 0s;
+ }
+
+ .bh-sl-close-directions-container {
+ width: 100%;
+ height: 20px;
+ position: relative;
+
+ .bh-sl-close-icon {
+ top: 0;
+ right: 6px;
+ }
+ }
+
+ .bh-sl-noresults-title {
+ font-weight: bold;
+ }
+ }
+
+ .loc-name {
+ /* Picked up by both list and infowindows */
+ font-size: 15px;
+ font-weight: bold;
+ }
+
+ .bh-sl-map {
+ height: 530px;
+ }
+
+ .bh-sl-pagination-container {
+ clear: both;
+
+ ol {
+ list-style-type: none;
+ text-align: center;
+ margin: 0;
+ padding: 10px 0;
+
+ li {
+ display: inline-block;
+ padding: 10px;
+ cursor: pointer;
+ font: bold 14px $arialFont;
+ color: $blue;
+ }
+
+ .bh-sl-current {
+ color: $textgray;
+ cursor: auto;
+ text-decoration: none;
+ }
+ }
+ }
+}
+
+/* Modal window */
+.bh-sl-overlay {
+ position: fixed;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 10000;
+ background: url(../img/overlay-bg.png) repeat;
+
+ .bh-sl-modal-window {
+ position: absolute;
+ left: 50%;
+ margin-left: -460px; /* width divided by 2 */
+ margin-top: 60px;
+ width: 920px;
+ height: 620px;
+ z-index: 10010;
+ background: $white;
+ border-radius: 10px;
+ box-shadow: 0 0 10px #656565;
+
+ .bh-sl-map-container {
+ margin-top: 50px; /* increase map container margin */
+ }
+
+ .bh-sl-modal-content {
+ float: left;
+ padding: 0 22px; /* there's already a margin on the top of the map-container div */
+ }
+
+ .bh-sl-close-icon {
+ top: 13px;
+ right: 22px;
+ }
+ }
+}
+
+.bh-sl-close-icon {
+ position: absolute;
+ cursor: pointer;
+ height: 24px;
+ width: 24px;
+
+ &:after,
+ &:before {
+ position: absolute;
+ top: 3px;
+ right: 3px;
+ bottom: 0;
+ left: 50%;
+ background: $gray;
+ content: '';
+ display: block;
+ height: 24px;
+ margin: -3px 0 0 -1px;
+ width: 3px;
+ -webkit-transform: rotate(45deg);
+ -moz-transform: rotate(45deg);
+ -ms-transform: rotate(45deg);
+ -o-transform: rotate(45deg);
+ transform: rotate(45deg);
+ }
+
+ &:hover:after,
+ &:hover:before {
+ background: darken($gray, 10%);
+ }
+
+ &:before {
+ -webkit-transform: rotate(-45deg);
+ -moz-transform: rotate(-45deg);
+ -ms-transform: rotate(-45deg);
+ -o-transform: rotate(-45deg);
+ transform: rotate(-45deg);
+ }
+}
diff --git a/src/css/storelocator.less b/src/css/storelocator.less
deleted file mode 100644
index dbaa64b..0000000
--- a/src/css/storelocator.less
+++ /dev/null
@@ -1,298 +0,0 @@
-@white: #fff;
-@gray: #ccc;
-@darkgray: #8e8e8e;
-@textgray: #333;
-@shadow: #656565;
-@darkergray: #2c2c2c;
-@black: #000;
-@orange: #e76737;
-@red: #ae2118;
-@darkred: #961f17;
-@maroon: #451400;
-@arialFont: Arial, Helvetica, sans-serif;
-
-/* #page-header it just included for the examples */
-#page-header {
- float: left;
- display: block;
-}
-
-/* Infowindow Roboto font override */
-.gm-style div, .gm-style span, .gm-style label, .gm-style a {
- font-family: @arialFont;
-}
-
-.bh-sl-error {
- clear: both;
- float: left;
- width: 100%;
- padding: 10px 0;
- color: @red;
- font-weight: bold;
-}
-
-.bh-sl-container {
- float: left;
- margin-left: 20px;
- width: 875px;
- font: normal 14px/20px @arialFont;
- color: @textgray;
-
- .bh-sl-form-container {
- clear: left;
- float: left;
- margin-top: 15px;
- width: 100%;
- }
-
- .form-input {
- float: left;
- margin-top: 3px;
-
- label {
- font-weight: bold;
- }
-
- input,select {
- margin: 0 15px 0 10px;
- padding: 6px 12px;
- line-height: 16px;
- border: 1px solid @gray;
- font: normal 14px/18px @arialFont;
- -webkit-border-radius: 4px;
- border-radius: 4px;
- }
- }
-
- button {
- float: left;
- cursor: pointer;
- margin-top: 3px;
- padding: 6px 12px;
- background: @red;
- border: 1px solid @darkred;
- font: normal 14px/18px @arialFont;
- color: @white;
- white-space: nowrap;
- -webkit-border-radius: 4px;
- border-radius: 4px;
- }
-
- .bh-sl-loading {
- float: left;
- margin: 4px 0 0 10px;
- width: 16px;
- height: 16px;
- background: url(../img/ajax-loader.gif) no-repeat;
- }
-
- .bh-sl-filters-container {
- clear: both;
- float: left;
- width: 100%;
- margin: 15px 0;
-
- .bh-sl-filters {
- list-style: none;
- float: left;
- padding: 0;
- margin: 0 100px 0 0;
-
- li {
- display: block;
- clear: left;
- float: left;
- width: 100%;
- margin: 5px 0;
-
- label {
- display: inline;
- }
-
- input {
- display: block;
- float: left;
- margin: 2px 8px 2px 0;
- }
- }
- }
- }
-
- .bh-sl-map-container {
- clear: left;
- float: left;
- margin-top: 27px;
- height: 530px;
- width: 875px;
-
- a {
- color: @orange;
- text-decoration: none;
- &:hover, &:active {
- text-decoration: underline;
- }
- }
- }
-
- .bh-sl-loc-list {
- float: left;
- width: 30%;
- height: 530px;
- overflow-x: auto;
-
- ul {
- display: block;
- clear: left;
- float: left;
- width: 100%;
- list-style: none;
- margin: 0;
- padding: 0;
-
- li {
- display: block;
- clear: left;
- float: left;
- margin: 3% 4%;
- cursor: pointer;
- width: 92%;
- border: 1px solid @white; /* Adding this to prevent moving li elements when adding the list-focus class*/
- }
- }
-
- .list-label {
- float: left;
- margin: 10px 0 0 6px;
- padding: 2px 3px;
- width: 10%;
- max-width: 25px;
- text-align: center;
- background: @maroon;
- color: @white;
- font-weight: bold;
- }
-
- .list-details {
- float: left;
- margin-left: 6px;
- width: 80%;
-
- .list-content {
- padding: 10px;
- }
-
- .loc-dist {
- font-weight: bold;
- font-style: italic;
- color: @darkgray;
- }
- }
-
- .list-focus {
- border: 1px solid rgba(150, 31, 23, 0.4);
- -moz-box-shadow: 0 0 8px rgba(150, 31, 23, 0.4);
- -webkit-box-shadow: 0 0 8px rgba(150, 31, 23, 0.4);
- box-shadow: 0 0 8px rgba(150, 31, 23, 0.4);
- transition: border 0.2s linear 0s, box-shadow 0.2s linear 0s;
- }
-
- .bh-sl-close-directions-container {
- width: 100%;
- height: 20px;
- position: relative;
-
- .bh-sl-close-icon {
- top: 0;
- right: 6px;
- }
- }
-
- .bh-sl-noresults-title {
- font-weight: bold;
- color: @red;
- }
- }
-
- .loc-name {
- /* Picked up by both list and infowindows */
- color: @red;
- font-weight: bold;
- }
-
- .bh-sl-map {
- float: left;
- width: 70%;
- height: 530px;
- }
-
- .bh-sl-pagination-container {
- clear: both;
-
- ol {
- list-style-type: none;
- text-align: center;
- margin: 0;
- padding: 10px 0;
-
- li {
- display: inline-block;
- padding: 10px;
- cursor: pointer;
- font: bold 14px @arialFont;
- color: @red;
- text-decoration: underline;
- }
-
- .bh-sl-current {
- color: @textgray;
- cursor: auto;
- text-decoration: none;
- }
- }
- }
-}
-
-/* Modal window */
-.bh-sl-overlay {
- position: fixed;
- left: 0px;
- top: 0px;
- width: 100%;
- height: 100%;
- z-index: 10000;
- background: url(../img/overlay-bg.png) repeat;
-
- .bh-sl-modal-window {
- position: absolute;
- left: 50%;
- margin-left: -460px; /* width divided by 2 */
- margin-top: 60px;
- width: 920px;
- height: 590px;
- z-index: 10010;
- background: @white;
- border-radius: 10px;
- box-shadow: 0 0 10px #656565;
-
- .bh-sl-modal-content {
- float: left;
- padding: 0 22px; /* there's already a margin on the top of the map-container div */
- }
-
- .bh-sl-close-icon {
- top: -6px;
- right: -6px;
- }
- }
-}
-
-.bh-sl-close-icon {
- position: absolute;
- width: 18px;
- height: 18px;
- cursor: pointer;
- background: @darkergray url(../img/close-icon.png) 3px 3px no-repeat;
- border: 1px solid @black;
- border-radius: 3px;
- box-shadow: 0 0 3px @shadow;
-}
\ No newline at end of file
diff --git a/src/css/storelocator.scss b/src/css/storelocator.scss
new file mode 100644
index 0000000..479f444
--- /dev/null
+++ b/src/css/storelocator.scss
@@ -0,0 +1,463 @@
+$white: #fff;
+$gray: #ccc;
+$darkgray: #6c6c6c;
+$titlegray: #797874;
+$shadow: #656565;
+$textgray: #555;
+$darkergray: #2c2c2c;
+$black: #000;
+$blue: #005293;
+$red: #ae2118;
+$arialFont: Arial, Helvetica, sans-serif;
+$tablet: '(min-width: 768px)';
+$tabletLarge: '(min-width: 1024px)';
+
+/* #page-header it just included for the examples */
+#page-header {
+ display: block;
+ float: left;
+ max-width: 800px;
+
+ .bh-sl-title {
+ color: $titlegray;
+ font: normal 20px/1.4 $arialFont;
+
+ @media #{$tabletLarge} {
+ font-size: 30px;
+ }
+ }
+}
+
+/* Infowindow Roboto font override */
+.gm-style {
+ div, span, label, a {
+ font-family: $arialFont;
+ }
+}
+
+.GMAMP-maps-pin-view {
+ color: $black;
+ font: 400 15px/1 $arialFont;
+}
+
+/* InfoBubble font size */
+.bh-sl-window {
+ font-size: 13px;
+}
+
+.bh-sl-error {
+ clear: both;
+ color: $red;
+ float: left;
+ font-weight: bold;
+ padding: 10px 0;
+ width: 100%;
+}
+
+/* Avoid image issues with Google Maps and CSS resets */
+.bh-sl-map-container img {
+ border-radius: 0 !important;
+ box-shadow: none !important;
+ max-height: none !important;
+ max-width: none !important;
+}
+
+.bh-sl-container {
+ box-sizing: border-box;
+ color: $textgray;
+ float: left;
+ font: normal 14px/1.4 $arialFont;
+ padding: 0 15px;
+ width: 100%;
+
+ /* Avoid issues with Google Maps and CSS frameworks */
+ > * {
+ box-sizing: content-box !important;
+ }
+
+ .bh-sl-form-container {
+ clear: left;
+ float: left;
+ margin-top: 15px;
+ width: 100%;
+ }
+
+ .form-input {
+ float: left;
+ margin-top: 3px;
+ width: 100%;
+
+ // Tablet up
+ @media #{$tablet} {
+ width: auto;
+ }
+
+ label {
+ display: block;
+ font-weight: bold;
+ width: 100%;
+
+ // Tablet up
+ @media #{$tablet} {
+ display: inline-block;
+ width: auto;
+ }
+ }
+
+ input, select {
+ box-sizing: border-box;
+ border: 1px solid $gray;
+ border-radius: 4px;
+ font: normal 14px/1.4 $arialFont;
+ margin: 15px 0;
+ padding: 6px 12px;
+ width: 100%;
+ -webkit-border-radius: 4px;
+
+ // Tablet up
+ @media #{$tablet} {
+ width: auto;
+ margin: 0 15px 0 10px;
+ }
+ }
+ }
+
+ .loc-alt-dist {
+ display: none;
+ }
+
+ button {
+ background: darken( $blue, 5% );
+ border: none;
+ border-radius: 4px;
+ color: $white;
+ cursor: pointer;
+ float: left;
+ font: bold 14px/1.4 $arialFont;
+ margin-top: 3px;
+ padding: 6px 12px;
+ white-space: nowrap;
+ -webkit-border-radius: 4px;
+ }
+
+ .bh-sl-loading {
+ background: url(../img/ajax-loader.gif) no-repeat;
+ float: left;
+ margin: 4px 0 0 10px;
+ height: 16px;
+ width: 16px;
+ }
+
+ .bh-sl-filters-container {
+ clear: both;
+ float: left;
+ margin: 15px 0;
+ width: 100%;
+
+ .bh-sl-filters {
+ float: left;
+ list-style: none;
+ margin: 0 100px 0 0;
+ padding: 0;
+
+ li {
+ clear: left;
+ display: block;
+ float: left;
+ margin: 5px 0;
+ width: 100%;
+
+ label {
+ display: inline;
+ vertical-align: text-bottom;
+ }
+
+ input {
+ display: block;
+ float: left;
+ margin-right: 8px;
+ }
+
+ select {
+ box-sizing: border-box;
+ border: 1px solid $gray;
+ border-radius: 4px;
+ font: normal 14px/1.4 $arialFont;
+ padding: 6px 12px;
+ -webkit-border-radius: 4px;
+ }
+ }
+ }
+ }
+
+ .bh-sl-map-container {
+ clear: left;
+ float: left;
+ margin-top: 27px;
+ width: 100%;
+
+ // Tablet large up
+ @media #{$tabletLarge} {
+ margin-bottom: 60px;
+ }
+
+ a {
+ color: $blue;
+ text-decoration: none;
+
+ &:active,
+ &:focus,
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+ }
+
+ .bh-sl-loc-list {
+ font-size: 13px;
+ height: 530px;
+ overflow-x: auto;
+ width: 100%;
+
+ // Tablet large up
+ @media #{$tabletLarge} {
+ width: 30%;
+ }
+
+ ul {
+ display: block;
+ clear: left;
+ float: left;
+ width: 100%;
+ list-style: none;
+ margin: 0;
+ padding: 0;
+
+ li {
+ border: 1px solid $white; /* Adding this to prevent moving li elements when adding the list-focus class*/
+ box-sizing: border-box;
+ clear: left;
+ cursor: pointer;
+ display: block;
+ float: left;
+ width: 100%;
+ }
+ }
+
+ .list-label {
+ background: darken( $blue, 20% );
+ border-radius: 15px;
+ color: $white;
+ display: block;
+ float: left;
+ font-weight: bold;
+ margin: 10px 0 0 15px;
+ padding: 4px 7px;
+ text-align: center;
+ width: auto;
+ min-width: 13px;
+ }
+
+ .list-details {
+ float: left;
+ margin-left: 6px;
+ width: 80%;
+
+ .list-content {
+ padding: 10px;
+ }
+
+ .loc-dist {
+ color: $darkgray;
+ font-weight: bold;
+ font-style: italic;
+ }
+ }
+
+ .list-focus {
+ border: 1px solid rgba(0, 82, 147, 0.4);
+ transition: border 0.2s linear 0s, box-shadow 0.2s linear 0s;
+ }
+
+ .bh-sl-close-directions-container {
+ height: 20px;
+ position: relative;
+ width: 100%;
+
+ .bh-sl-close-icon {
+ right: 6px;
+ top: 0;
+ }
+ }
+
+ .bh-sl-directions-panel {
+ margin: 0 2%;
+
+ /* Avoid issues with table-layout */
+ table {
+ table-layout: auto;
+ width: 100%;
+ }
+
+ table, td {
+ vertical-align: middle;
+ border-collapse: separate;
+ }
+
+ td {
+ padding: 1px;
+ }
+
+ .adp-placemark {
+ margin: 10px 0;
+ border: 1px solid #c0c0c0;
+ }
+
+ .adp-marker {
+ padding: 3px;
+ }
+ }
+
+ .bh-sl-noresults-title {
+ font-weight: bold;
+ margin: 15px;
+ }
+
+ .bh-sl-noresults-desc {
+ margin: 0 15px;
+ }
+ }
+
+ .loc-name {
+ /* Picked up by both list and infowindows */
+ font-size: 15px;
+ font-weight: bold;
+ }
+
+ .bh-sl-map {
+ float: left;
+ height: 530px;
+ width: 100%;
+
+ // Tablet large up
+ @media #{$tabletLarge} {
+ width: 70%;
+ }
+ }
+
+ .bh-sl-pagination-container {
+ clear: both;
+
+ ol {
+ list-style-type: none;
+ margin: 0;
+ padding: 10px 0;
+ text-align: center;
+
+ li {
+ display: inline-block;
+ margin: 0 4px;
+ }
+
+ a {
+ box-shadow: none;
+ color: $blue;
+ display: inline-block;
+ font: bold 14px $arialFont;
+ padding: 10px;
+ text-decoration: underline;
+ }
+
+ .bh-sl-current {
+ color: $textgray;
+ cursor: auto;
+ text-decoration: none;
+
+ a {
+ color: $textgray;
+ pointer-events: none;
+ text-decoration: none;
+ }
+ }
+ }
+ }
+}
+
+/* Modal window */
+.bh-sl-overlay {
+ background: url(../img/overlay-bg.png) repeat;
+ height: 100%;
+ left: 0;
+ position: fixed;
+ top: 0;
+ width: 100%;
+ z-index: 10000;
+
+ .bh-sl-modal-window {
+ background: $white;
+ border-radius: 10px;
+ box-shadow: 0 0 10px #656565;
+ position: absolute;
+ left: 50%;
+ margin-left: -460px; /* width divided by 2 */
+ margin-top: 60px;
+ height: 620px;
+ width: 920px;
+ z-index: 10010;
+
+ .bh-sl-map-container {
+ margin-top: 50px; /* increase map container margin */
+ }
+
+ .bh-sl-modal-content {
+ float: left;
+ padding: 0 1%; /* there's already a margin on the top of the map-container div */
+ width: 98%;
+ }
+
+ .bh-sl-close-icon {
+ right: 22px;
+ top: 13px;
+ }
+ }
+}
+
+.bh-sl-close-icon {
+ cursor: pointer;
+ height: 24px;
+ position: absolute;
+ width: 24px;
+
+ &:after,
+ &:before {
+ background: $gray;
+ content: '';
+ display: block;
+ height: 24px;
+ margin: -3px 0 0 -1px;
+ position: absolute;
+ bottom: 0;
+ left: 50%;
+ right: 3px;
+ top: 3px;
+ width: 3px;
+ -webkit-transform: rotate(45deg);
+ -moz-transform: rotate(45deg);
+ -ms-transform: rotate(45deg);
+ -o-transform: rotate(45deg);
+ transform: rotate(45deg);
+ }
+
+ &:hover:after,
+ &:hover:before {
+ background: darken($gray, 10%);
+ }
+
+ &:before {
+ -webkit-transform: rotate(-45deg);
+ -moz-transform: rotate(-45deg);
+ -ms-transform: rotate(-45deg);
+ -o-transform: rotate(-45deg);
+ transform: rotate(-45deg);
+ }
+}
diff --git a/src/js/jquery.storelocator.js b/src/js/jquery.storelocator.js
index 25ea096..efdf78b 100644
--- a/src/js/jquery.storelocator.js
+++ b/src/js/jquery.storelocator.js
@@ -1,104 +1,147 @@
-/* global define, window, document, Handlebars, google */
-
;(function ($, window, document, undefined) {
'use strict';
var pluginName = 'storeLocator';
+ var googleMapsScriptIsInjected = false;
+ var googleMapsAPIPromise = null;
// Only allow for one instantiation of this script
if (typeof $.fn[pluginName] !== 'undefined') {
return;
}
- // Variables used across multiple methods
- var $this, listTemplate, infowindowTemplate, dataTypeRead, originalOrigin, originalData, originalZoom, dataRequest, searchInput, addressInput, olat, olng, storeNum, directionsDisplay, directionsService;
+ // 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, reload, nameAttrs, originalFilterVals, paginationPage, locationsTotal;
var featuredset = [], locationset = [], normalset = [], markers = [];
- var filters = {}, locationData = {}, GeoCodeCalc = {}, mappingObj = {};
+ var filters = {}, locationData = {}, GeoCodeCalc = {}, mappingObj = {}, disabledFilterVals = {};
// 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' : {
+ 'ajaxData' : null,
+ 'altDistanceNoResult' : false,
+ 'apiKey' : null,
+ 'autoComplete' : false,
+ 'autoCompleteDisableListener': false,
+ 'autoCompleteOptions' : {},
+ 'autoGeocode' : false,
+ 'bounceMarker' : true, // Deprecated.
+ 'catMarkers' : null,
+ 'dataLocation' : 'data/locations.json',
+ 'dataRaw' : null,
+ 'dataType' : 'json',
+ 'debug' : false,
+ 'defaultLat' : null,
+ 'defaultLng' : null,
+ 'defaultLoc' : false,
+ 'disableAlphaMarkers' : false,
+ 'distanceAlert' : 60,
+ 'dragSearch' : false,
+ 'exclusiveFiltering' : false,
+ 'exclusiveTax' : null,
+ 'featuredDistance' : null,
+ 'featuredLocations' : false,
+ 'fullMapStart' : false,
+ 'fullMapStartBlank' : false,
+ 'fullMapStartListLimit' : false,
+ 'infoBubble' : null,
+ 'inlineDirections' : false,
+ 'lazyLoadMap' : false,
+ 'lengthUnit' : 'm',
+ 'listColor1' : '#ffffff',
+ 'listColor2' : '#eeeeee',
+ 'loading' : false,
+ 'locationsPerPage' : 10,
+ 'mapSettings' : {
+ mapTypeId: 'roadmap',
zoom : 12,
- mapTypeId: google.maps.MapTypeId.ROADMAP
- },
- 'markerImg' : null,
- 'markerDim' : null,
- 'catMarkers' : null,
- 'lengthUnit' : 'm',
- 'storeLimit' : 26,
- 'distanceAlert' : 60,
- 'dataType' : 'xml',
- 'dataLocation' : 'data/locations.xml',
- '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,
- 'autoGeocode' : false,
- 'maxDistance' : false,
- 'maxDistanceID' : 'bh-sl-maxdistance',
- 'fullMapStart' : 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',
+ },
+ 'mapSettingsID' : '',
+ 'markerCluster' : null,
+ 'markerImg' : null,
+ 'markerDim' : null,
+ 'maxDistance' : false,
+ 'modal' : false,
+ 'nameAttribute' : 'name',
+ 'nameSearch' : false,
+ 'noForm' : false,
+ 'openNearest' : false,
+ 'originMarker' : false,
+ 'originMarkerDim' : null,
+ 'originMarkerImg' : null,
+ 'pagination' : false,
+ 'querystringParams' : false,
+ 'selectedMarkerImg' : null,
+ 'selectedMarkerImgDim' : null,
+ 'sessionStorage' : false,
+ 'slideMap' : true,
+ 'sortBy' : null,
+ 'storeLimit' : 26,
+ 'taxonomyFilters' : null,
+ 'visibleMarkersList' : false,
+ 'xmlElement' : 'marker',
+ // HTML elements
+ 'addressID' : 'bh-sl-address',
+ 'closeIcon' : 'bh-sl-close-icon',
+ 'formContainer' : 'bh-sl-form-container',
+ 'formID' : 'bh-sl-user-location',
+ 'geocodeID' : null,
+ 'lengthSwapID' : 'bh-sl-length-swap',
+ 'loadingContainer' : 'bh-sl-loading',
+ 'locationList' : 'bh-sl-loc-list',
+ 'mapID' : 'bh-sl-map',
+ 'maxDistanceID' : 'bh-sl-maxdistance',
+ 'modalContent' : 'bh-sl-modal-content',
+ 'modalWindow' : 'bh-sl-modal-window',
+ 'orderID' : 'bh-sl-order',
+ 'overlay' : 'bh-sl-overlay',
+ 'regionID' : 'bh-sl-region',
+ 'searchID' : 'bh-sl-search',
+ 'sortID' : 'bh-sl-sort',
+ 'taxonomyFiltersContainer': 'bh-sl-filters-container',
+ // Templates
'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',
- 'querystringParams' : false,
- 'callbackNotify' : null,
- 'callbackBeforeSend' : null,
- 'callbackSuccess' : null,
- 'callbackModalOpen' : null,
- 'callbackModalReady' : null,
- 'callbackModalClose' : null,
- 'callbackJsonp' : null,
- 'callbackPageChange' : null,
- 'callbackDirectionsRequest': null,
- 'callbackCloseDirections' : null,
- 'callbackNoResults' : null,
- 'callbackListClick' : null,
- 'callbackMarkerClick' : null,
+ // Callbacks
+ 'callbackAutoGeoSuccess' : null,
+ 'callbackBeforeMapInject' : null,
+ 'callbackBeforeSend' : null,
+ 'callbackCloseDirections' : null,
+ 'callbackCreateMarker' : null,
+ 'callbackDirectionsRequest' : null,
+ 'callbackFilters' : null,
+ 'callbackFormVals' : null,
+ 'callbackGeocodeRestrictions': null,
+ 'callbackJsonp' : null,
+ 'callbackListClick' : null,
+ 'callbackMapSet' : null,
+ 'callbackMarkerClick' : null,
+ 'callbackModalClose' : null,
+ 'callbackModalOpen' : null,
+ 'callbackModalReady' : null,
+ 'callbackNearestLoc' : null,
+ 'callbackNoResults' : null,
+ 'callbackNotify' : null,
+ 'callbackOrder' : null,
+ 'callbackPageChange' : null,
+ 'callbackRegion' : null,
+ 'callbackSorting' : null,
+ 'callbackSuccess' : 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'
+ '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 ',
+ 'kilometerLang' : 'kilometer',
+ 'kilometersLang' : 'kilometers',
+ 'mileLang' : 'mile',
+ 'milesLang' : 'miles',
+ 'noResultsTitle' : 'No results',
+ 'noResultsDesc' : 'No locations were found with the given criteria. Please modify your selections or input.',
+ 'nextPage' : 'Next »',
+ 'prevPage' : '« Prev'
};
// Plugin constructor
@@ -108,16 +151,51 @@
this.settings = $.extend({}, defaults, options);
this._defaults = defaults;
this._name = pluginName;
- this.init();
+
+ // Add Map ID to map settings if set.
+ if (this.settings.mapSettingsID !== '') {
+ this.settings.mapSettings.mapId = this.settings.mapSettingsID;
+ }
+
+ // Load Google Maps API when lazy load is enabled.
+ if (this.settings.lazyLoadMap && this.settings.apiKey !== null && typeof google === 'undefined') {
+ var _this = this;
+ var optionsQuery = {};
+ var loadMap = false;
+
+ // Load new marker library.
+ optionsQuery.libraries = 'marker';
+
+ // Autocomplete.
+ if (this.settings.autoComplete === true) {
+ optionsQuery.libraries = 'places,marker';
+ }
+
+ // Allow callback to resolve map loading when set.
+ if (this.settings.callbackBeforeMapInject) {
+ new Promise(function (resolve, reject) {
+ _this.settings.callbackBeforeMapInject.call(this, options, resolve);
+ }).then(function () {
+ _this.triggerMapLoad(optionsQuery)
+ });
+ } else {
+ _this.triggerMapLoad(optionsQuery)
+ }
+ } else {
+ 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
@@ -136,10 +214,8 @@
dataTypeRead = this.settings.dataType;
}
- // Set up the directionsService if it's true
- if(this.settings.inlineDirections === true) {
- directionsDisplay = new google.maps.DirectionsRenderer();
- directionsService = new google.maps.DirectionsService();
+ // Add directions panel if enabled
+ if (this.settings.inlineDirections === true) {
$('.' + this.settings.locationList).prepend('
');
}
@@ -148,19 +224,28 @@
// Add Handlebars helper for handling URL output
Handlebars.registerHelper('niceURL', function(url) {
- if(url){
+ if (url) {
return url.replace('https://', '').replace('http://', '');
}
});
+ // Handle distance changes on select
+ if (this.settings.maxDistance === true) {
+ this.distanceFiltering();
+ }
+
// Do taxonomy filtering if set
if (this.settings.taxonomyFilters !== null) {
this.taxonomyFiltering();
}
+ // Do sorting and ordering if set.
+ this.sorting();
+ this.order();
+
// 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
+ // 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);
@@ -171,21 +256,110 @@
$('.' + 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 && this.settings.autoCompleteDisableListener !== true) {
+ autoPlaces.addListener('place_changed', function(e) {
+ _this.processForm(e);
+ });
+ }
+ }
+
// Load the templates and continue from there
this._loadTemplates();
},
+ /**
+ * Trigger async map loading
+ */
+ triggerMapLoad: function(optionsQuery) {
+ this.writeDebug('triggerMapLoad');
+ var _this = this;
+
+ this.loadMapsAPI(this.settings.apiKey, optionsQuery)
+ .then(function (map) {
+ _this.map = map;
+ _this.init();
+ });
+ },
+
+ /**
+ * Inject Google Maps script
+ *
+ * @param {Object} options Options query object to pass as query string parameters to Google Maps.
+ */
+ injectGoogleMapsScript: function (options) {
+ this.writeDebug('injectGoogleMapsScript');
+ options = (typeof options !== 'undefined') ? options : {};
+
+ if (googleMapsScriptIsInjected) {
+ throw new Error('Google Maps API is already loaded.');
+ }
+
+ var optionsQuery = Object.keys(options)
+ .map(k => encodeURIComponent(k) + '=' + encodeURIComponent(options[k]))
+ .join('&');
+ var apiURL = 'https://maps.googleapis.com/maps/api/js?' + optionsQuery;
+ var mapScript = document.createElement('script');
+ mapScript.setAttribute('src', apiURL);
+ mapScript.setAttribute('async', '');
+ mapScript.setAttribute('defer', '');
+
+ // Append the script to the document head.
+ document.head.appendChild(mapScript);
+ googleMapsScriptIsInjected = true;
+ },
+
+ /**
+ * Load Google Maps API
+ *
+ * @param {string} apiKey Google Maps JavaScript API key.
+ * @param {Object} options Options query object to pass as query string parameters to Google Maps.
+ *
+ * @returns {Promise}
+ */
+ loadMapsAPI: function (apiKey, options) {
+ this.writeDebug('loadMapsAPI');
+ options = (typeof options !== 'undefined') ? options : {};
+ var _this = this;
+
+ if (!googleMapsAPIPromise) {
+ googleMapsAPIPromise = new Promise(function (resolve, reject) {
+ try {
+ window.onGoogleMapsAPILoaded = resolve;
+
+ _this.injectGoogleMapsScript({
+ key: apiKey,
+ loading: 'async',
+ callback: 'onGoogleMapsAPILoaded',
+ ...options,
+ });
+ } catch (error) {
+ reject(error);
+ }
+ }).then(function () { window.google.maps });
+ }
+
+ return googleMapsAPIPromise;
+ },
+
/**
* Destroy
- * Note: The Google map is not destroyed here because Google recommends using a single instance and reusing it (it's not really supported)
+ * 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) {
+ if (markers.length) {
for(var i = 0; i <= markers.length; i++) {
google.maps.event.removeListener(markers[i]);
}
@@ -193,7 +367,7 @@
// Remove markup
$('.' + this.settings.locationList + ' ul').empty();
- if($mapDiv.hasClass('bh-sl-map-open')) {
+ if ($mapDiv.hasClass('bh-sl-map-open')) {
$mapDiv.empty().removeClass('bh-sl-map-open');
}
@@ -214,28 +388,100 @@
// 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 ) {
+
+ 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 a');
+ }
+ },
+
+ /**
+ * 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();
+ reload = true;
+
+ if (this.settings.taxonomyFilters !== null) {
+ this.formFiltersReset();
+ this.resetDisabledFilterVals();
+ 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);
}
@@ -243,27 +489,60 @@
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) ))));
},
+ /**
+ * Range helper function for coordinate validation
+ *
+ * @param min {number} minimum number allowed
+ * @param num {number} number to check
+ * @param max {number} maximum number allowed
+ *
+ * @returns {boolean}
+ */
+ inRange: function(min, num, max){
+ this.writeDebug('inRange',arguments);
+ num = Math.abs(num);
+ return isFinite(num) && (num >= min) && (num <= max);
+ },
+
+ /**
+ * Coordinate validation
+ *
+ * @param lat {number} latitude
+ * @param lng {number} longitude
+ *
+ * @returns {boolean}
+ */
+ coordinatesInRange: function (lat, lng) {
+ this.writeDebug('coordinatesInRange',arguments);
+ return this.inRange(-90, lat, 90) && this.inRange(-180, lng, 180);
+ },
+
/**
* 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 + '=([^]*)'),
@@ -272,10 +551,20 @@
}
},
+ /**
+ * 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 = '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.
';
@@ -289,7 +578,7 @@
source = template;
infowindowTemplate = Handlebars.compile(source);
}),
-
+
// KML locations list
$.get(this.settings.KMLlistTemplatePath, function (template) {
source = template;
@@ -325,7 +614,7 @@
source = template;
infowindowTemplate = Handlebars.compile(source);
}),
-
+
// Locations list
$.get(this.settings.listTemplatePath, function (template) {
source = template;
@@ -347,6 +636,7 @@
* 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();
@@ -360,13 +650,14 @@
* 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('keyup.'+pluginName, function (e) {
+ $(document).on('keydown.'+pluginName, function (e) {
if (e.keyCode === 13 && $('#' + _this.settings.addressID).is(':focus')) {
_this.processForm(e);
}
@@ -377,97 +668,246 @@
_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();
+ });
+ }
+
+ // Track changes to the address search field.
+ $('#' + this.settings.addressID).on('change.'+pluginName, function () {
+ originalFilterVals = undefined;
+ disabledFilterVals = {};
+
+ // Unset origin tracking if input field is removed.
+ if (
+ $.trim($('#' + _this.settings.addressID).val()) === '' &&
+ (typeof searchInput === 'undefined' || searchInput === '')
+ ) {
+
+ // Reset the origin, mapping object, and disabled filter values.
+ if (_this.settings.taxonomyFilters !== null && _this.settings.exclusiveFiltering === false) {
+ olat = undefined;
+ olng = undefined;
+ originalOrigin = undefined;
+ mappingObj = {};
+ _this.resetDisabledFilterVals();
+ _this.taxonomyFiltersInit();
+ _this.mapping(null);
+ }
+ }
+ });
},
/**
* 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
+ * @param map (object} Google Maps object.
+ *
* @returns {Object} deferred object
*/
- _getData: function (lat, lng, address) {
- var _this = this;
- var d = $.Deferred();
-
+ _getData: function (lat, lng, address, geocodeData, map) {
+ 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);
+ this.settings.callbackBeforeSend.call(this, lat, lng, address, formattedAddress, northEast, southWest, map);
}
- // Loading
- if(this.settings.loading === true){
- $('.' + this.settings.formContainer).append('
');
+ // 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 JSON.parse(_this.settings.dataRaw);
+ }
+ else {
+ return [];
+ }
+
+ }
}
+ // Remote data
+ else {
+ var d = $.Deferred();
+
+ // Loading
+ if (this.settings.loading === true) {
+ $('.' + this.settings.formContainer).append('
');
+ }
- // AJAX request
- $.ajax({
- type : 'GET',
- url : this.settings.dataLocation + (this.settings.dataType === 'jsonp' ? (this.settings.dataLocation.match(/\?/) ? '&' : '?') + 'callback=?' : ''),
- // Passing the lat, lng, and address with the AJAX request so they can optionally be used by back-end languages
- data: {
+ // Data to pass with the AJAX request
+ var ajaxData = {
'origLat' : lat,
'origLng' : lng,
- 'origAddress': address
- },
- dataType : dataTypeRead,
- jsonpCallback: (this.settings.dataType === 'jsonp' ? this.settings.callbackJsonp : null)
- }).done(function (p) {
- d.resolve(p);
+ 'origAddress': address,
+ 'formattedAddress': formattedAddress,
+ 'boundsNorthEast' : northEast,
+ 'boundsSouthWest' : southWest
+ };
- // Loading remove
- if(_this.settings.loading === true){
- $('.' + _this.settings.formContainer + ' .' + _this.settings.loadingContainer).remove();
- }
- }).fail(d.reject);
- return d.promise();
+ // Set up extra object for custom extra data to be passed with the AJAX request
+ if (this.settings.ajaxData !== null && typeof this.settings.ajaxData === 'object') {
+ $.extend(ajaxData, this.settings.ajaxData);
+ }
+
+ // 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 : ajaxData,
+ 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 () {
- var _this = this;
- // If a default location is set
- if (this.settings.defaultLoc === true) {
- // 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) {
- var originAddress = data.address;
- mappingObj.lat = _this.settings.defaultLat;
- mappingObj.lng = _this.settings.defaultLng;
- mappingObj.origin = originAddress;
- _this.mapping(mappingObj);
- } else {
- // Unable to geocode
- _this.notify(_this.settings.addressErrorAlert);
- }
+ this.writeDebug('_start');
+ var _this = this,
+ doAutoGeo = this.settings.autoGeocode,
+ latlng;
+
+ // 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
+ window.addEventListener('resize', function() {
+ var center = _this.map.getCenter();
+ google.maps.event.trigger(_this.map, 'resize');
+ _this.map.setCenter(center);
});
- }
- // If show full map option is true
- if (this.settings.fullMapStart === true) {
- if(this.settings.querystringParams === true && this.getQueryString(this.settings.addressID) || this.getQueryString(this.settings.searchID)) {
- this.processForm(null);
- }
- else {
- this.mapping(null);
+ // 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 && this.settings.defaultLoc === false) {
+ 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 geolocation API option
- if (this.settings.autoGeocode === true) {
- if (navigator.geolocation) {
- navigator.geolocation.getCurrentPosition(function(position){
- // Have to do this to get around scope issues
- _this.autoGeocodeQuery(position);
- }, function(error){
- _this._autoGeocodeError(error);
- });
- }
+ // 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));
+ }
+
+ // Callback
+ if (_this.settings.callbackAutoGeoSuccess) {
+ _this.settings.callbackAutoGeoSuccess.call(this, pos);
+ }
+
+ _this.autoGeocodeQuery(pos);
+ }, function(error){
+ _this._autoGeocodeError(error);
+ });
}
},
@@ -475,7 +915,7 @@
* Geocode function used to geocode the origin (entered location)
*/
googleGeocode: function (thisObj) {
- var _this = thisObj;
+ thisObj.writeDebug('googleGeocode',arguments);
var geocoder = new google.maps.Geocoder();
this.geocode = function (request, callbackFunction) {
geocoder.geocode(request, function (results, status) {
@@ -483,6 +923,7 @@
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);
@@ -496,7 +937,7 @@
* Reverse geocode to get address for automatic options needed for directions link
*/
reverseGoogleGeocode: function (thisObj) {
- var _this = thisObj;
+ thisObj.writeDebug('reverseGoogleGeocode',arguments);
var geocoder = new google.maps.Geocoder();
this.geocode = function (request, callbackFunction) {
geocoder.geocode(request, function (results, status) {
@@ -504,6 +945,7 @@
if (results[0]) {
var result = {};
result.address = results[0].formatted_address;
+ result.fullResult = results[0];
callbackFunction(result);
}
} else {
@@ -519,9 +961,11 @@
*
* @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);
},
@@ -529,9 +973,11 @@
* 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;
@@ -542,16 +988,18 @@
/**
* 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) {
+ if (obj.hasOwnProperty(key)) {
+ if (obj[key] !== '' && obj[key].length !== 0) {
objTest = false;
}
}
@@ -560,18 +1008,36 @@
return objTest;
},
+ /**
+ * Checks to see if only a single taxonomy group has a selected value
+ *
+ * @param obj {Object} the object to check
+ * @param key {string} Key value of current filter group
+ *
+ * @returns {boolean}
+ */
+ hasSingleGroupFilterVal: function(obj, key) {
+ this.writeDebug('hasSingleGroupFilterVal',arguments);
+
+ // Copy the object so the original doesn't change.
+ var objCopy = Object.assign({}, obj);
+
+ return !this.hasEmptyObjectVals(objCopy[key]);
+ },
+
/**
* 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);
@@ -586,13 +1052,15 @@
* @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') {
+ if (key === 'distance' || key === 'altdistance') {
value = this.roundNumber(value, 2);
}
@@ -602,88 +1070,321 @@
},
/**
- * Location distance sorting function
+ * Location alphabetical sorting function
*
* @param locationsarray {array} locationset array
*/
- sortNumerically: function (locationsarray) {
- locationsarray.sort(function (a, b) {
- return ((a.distance < b.distance) ? -1 : ((a.distance > b.distance) ? 1 : 0));
- });
+ sortAlpha: function(locationsarray) {
+ this.writeDebug('sortAlpha',arguments);
+ var property = (this.settings.sortBy.hasOwnProperty('prop') && typeof this.settings.sortBy.prop !== 'undefined') ? this.settings.sortBy.prop : 'name';
+
+ if (this.settings.sortBy.hasOwnProperty('order') && this.settings.sortBy.order.toString() === 'desc') {
+ locationsarray.sort(function (a, b) {
+ return b[property].toLowerCase().localeCompare(a[property].toLowerCase());
+ });
+ } else {
+ locationsarray.sort(function (a, b) {
+ return a[property].toLowerCase().localeCompare(b[property].toLowerCase());
+ });
+ }
},
/**
- * Filter the data with Regex
+ * Location date sorting function
*
- * @param data {array} data array to check for filter values
- * @param filters {Object} taxonomy filters object
- * @returns {boolean}
+ * @param locationsarray {array} locationset array
*/
- filterData: function (data, filters) {
- var filterTest = true;
+ sortDate: function(locationsarray) {
+ this.writeDebug('sortDate',arguments);
+ var property = (this.settings.sortBy.hasOwnProperty('prop') && typeof this.settings.sortBy.prop !== 'undefined') ? this.settings.sortBy.prop : 'date';
- for (var k in filters) {
- if (filters.hasOwnProperty(k)) {
- if (!(new RegExp(filters[k].join(''), 'i').test(data[k]))) {
- filterTest = false;
- }
- }
+ if (this.settings.sortBy.hasOwnProperty('order') && this.settings.sortBy.order.toString() === 'desc') {
+ locationsarray.sort(function (a, b) {
+ return new Date(b[property]).getTime() - new Date(a[property]).getTime();
+ });
+ } else {
+ locationsarray.sort(function (a, b) {
+ return new Date(a[property]).getTime() - new Date(b[property]).getTime();
+ });
}
+ },
- if (filterTest) {
- return true;
+ /**
+ * Location distance sorting function
+ *
+ * @param locationsarray {array} locationset array
+ * @param distanceOverride {boolean} Force sort by distance
+ */
+ sortNumerically: function (locationsarray, distanceOverride) {
+ this.writeDebug('sortNumerically',arguments);
+ var property = (
+ this.settings.sortBy !== null &&
+ this.settings.sortBy.hasOwnProperty('prop') &&
+ typeof this.settings.sortBy.prop !== 'undefined'
+ ) ? this.settings.sortBy.prop : 'distance';
+
+ if (typeof distanceOverride !== 'undefined' && distanceOverride === true) {
+ property = 'distance';
+ }
+
+ if (this.settings.sortBy !== null && this.settings.sortBy.hasOwnProperty('order') && this.settings.sortBy.order.toString() === 'desc') {
+ locationsarray.sort(function (a, b) {
+ return ((b[property] < a[property]) ? -1 : ((b[property] > a[property]) ? 1 : 0));
+ });
+ } else {
+ locationsarray.sort(function (a, b) {
+ return ((a[property] < b[property]) ? -1 : ((a[property] > b[property]) ? 1 : 0));
+ });
}
},
/**
- * Build pagination numbers and next/prev links - private
+ * Alternative sorting setup
*
- * @param currentPage {number}
- * @param totalPages {number}
- * @returns {string}
+ * @param locationsarray {array} locationset array
*/
- _paginationOutput: function(currentPage, totalPages) {
-
- currentPage = parseFloat(currentPage);
- var output = '';
- var nextPage = currentPage + 1;
- var prevPage = currentPage - 1;
+ sortCustom: function (locationsarray) {
+ this.writeDebug('sortCustom',arguments);
- // Previous page
- if( currentPage > 0 ) {
- output += '' + this.settings.prevPage + ' ';
+ // Alphabetically, date, or numeric
+ if (this.settings.sortBy.hasOwnProperty('method') && this.settings.sortBy.method.toString() === 'alpha') {
+ this.sortAlpha(locationsarray);
+ } else if (this.settings.sortBy.hasOwnProperty('method') && this.settings.sortBy.method.toString() === 'date') {
+ this.sortDate(locationsarray);
+ } else {
+ this.sortNumerically(locationsarray);
}
+ },
- // Add the numbers
- for (var p = 0; p < Math.ceil(totalPages); p++) {
- var n = p + 1;
+ /**
+ * Run the matching between regular expression filters and string value
+ *
+ * @param filter {array} One or multiple filters to apply
+ * @param val {string} Value to compare
+ * @param inclusive {boolean} Inclusive (default) or exclusive
+ *
+ * @returns {boolean}
+ */
+ filterMatching: function(filter, val, inclusive) {
+ this.writeDebug('inclusiveFilter',arguments);
+ inclusive = (typeof inclusive !== 'undefined') ? inclusive : true;
+ var applyFilters;
- if (p === currentPage) {
- output += '' + n + ' ';
- }
- else {
- output += '' + n + ' ';
- }
+ // Undefined check.
+ if (typeof val === 'undefined') {
+ return false;
}
- // Next page
- if( nextPage < totalPages ) {
- output += '' + this.settings.nextPage + ' ';
+ // Modify the join depending on inclusive (AND) vs exclusive (OR).
+ if ( true === inclusive ) {
+ applyFilters = filter.join('');
+ } else {
+ applyFilters = filter.join('|');
}
- return output;
+ if ((new RegExp(applyFilters, 'i').test(val.replace(/([.*+?^=!:${}()|\[\]\/\\]|&\s+)/g, '')))) {
+ return true;
+ }
+
+ return false;
},
/**
- * Set up the pagination pages
+ * 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)) {
+ var testResults = [];
+
+ for (var l = 0; l < filters[k].length; l++) {
+
+ // Exclusive filtering
+ if (this.settings.exclusiveFiltering === true || (this.settings.exclusiveTax !== null && Array.isArray(this.settings.exclusiveTax) && this.settings.exclusiveTax.indexOf(k) !== -1)) {
+ testResults[l] = this.filterMatching(filters[k], data[k], false);
+ }
+ // Inclusive filtering
+ else {
+ testResults[l] = this.filterMatching(filters[k], data[k]);
+ }
+ }
+
+ // First handle name search, then standard filtering.
+ if (typeof nameAttrs !== 'undefined' && nameAttrs.indexOf(k) !== -1 && testResults.indexOf(true) !== -1) {
+ return true;
+ } else {
+ if (testResults.indexOf(true) === -1) {
+ filterTest = false;
+ }
+ }
+ }
+ }
+
+ return filterTest;
+ },
+
+ /**
+ * 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 = parseInt(currentPage);
+ totalPages = Math.ceil(totalPages);
+ var pagesStart, pagesEnd;
+ var output = '';
+ var nextPage = currentPage + 1;
+ var prevPage = currentPage - 1;
+ var pagesCutoff = 5;
+ var pagesCeiling = Math.ceil(pagesCutoff / 2);
+ var pagesFloor = Math.floor(pagesCutoff / 2);
+
+ // Determine page numbers to display.
+ if (totalPages < pagesCutoff) {
+ pagesStart = 0;
+ pagesEnd = totalPages;
+ } else if (currentPage >= 0 && currentPage <= pagesCeiling) {
+ pagesStart = 0;
+ pagesEnd = pagesCutoff;
+ } else if ((currentPage + pagesFloor) >= totalPages) {
+ pagesStart = (totalPages - pagesCutoff);
+ pagesEnd = totalPages;
+ } else {
+ pagesStart = (currentPage - pagesCeiling);
+ pagesEnd = (currentPage + pagesFloor);
+ }
+
+ // Previous page
+ if ( currentPage > 0 ) {
+ output += '';
+ output += '' + this.settings.prevPage + ' ';
+ output += ' ';
+ }
+
+ // Additional pages indicator.
+ if ((currentPage + 1) >= pagesCutoff && totalPages > pagesCutoff) {
+ // First page link.
+ output += '';
+ output += '' + 1 + ' ';
+ output += ' ';
+
+ output += '';
+ output += '… ';
+ output += ' ';
+ }
+
+ // Add the numbers
+ for (var p = pagesStart; p < pagesEnd; p++) {
+ var n = p + 1;
+
+ if (p === currentPage) {
+ output += '';
+ output += '' + n + ' ';
+ output += ' ';
+ } else {
+ output += '';
+ output += '' + n + ' ';
+ output += ' ';
+ }
+ }
+
+ // Additional pages indicator.
+ if ((currentPage + pagesCeiling) <= totalPages && totalPages > pagesCutoff) {
+ output += '';
+ output += '… ';
+ output += ' ';
+
+ // Last page link.
+ output += '';
+ output += '' + totalPages + ' ';
+ output += ' ';
+ }
+
+ // Next page
+ if ( nextPage < totalPages ) {
+ output += '';
+ output += '' + this.settings.nextPage + ' ';
+ output += ' ';
+ }
+
+ return output;
+ },
+
+ /**
+ * Reset pagination after the input has changed
+ */
+ paginationReset: function() {
+ this.writeDebug('paginationReset',arguments);
+
+ var currentUrl = window.location.href;
+ var url = new URL(currentUrl);
+
+ // Remove the old page in the URL.
+ url.searchParams.delete('bhsl-page');
+
+ // Update the query string param to match the new value.
+ if (history.pushState) {
+ window.history.pushState({path: url.href}, '', url.href);
+ }
+ },
+
+ /**
+ * Determine the total number of pages for pagination
+ */
+ totalPages: function() {
+ this.writeDebug('totalPages',arguments);
+
+ // Location limit.
+ if (
+ typeof originalOrigin !== 'undefined' &&
+ this.settings.storeLimit > 0 &&
+ locationset.length > this.settings.storeLimit
+ ) {
+ return this.settings.storeLimit / this.settings.locationsPerPage;
+ }
+
+ // WP API response after search.
+ if (locationsTotal > 0) {
+ return locationsTotal / this.settings.locationsPerPage;
+ }
+
+ // Unlimited or last page.
+ if (
+ this.settings.storeLimit === -1 ||
+ locationset.length < this.settings.storeLimit
+ ) {
+ return locationset.length / this.settings.locationsPerPage;
+ } else {
+ return this.settings.storeLimit / this.settings.locationsPerPage;
+ }
+ },
+
+ /**
+ * Set up the pagination pages
*
* @param currentPage {number} optional current page
*/
paginationSetup: function (currentPage) {
+ this.writeDebug('paginationSetup',arguments);
var pagesOutput = '';
- var totalPages = locationset.length / this.settings.locationsPerPage;
var $paginationList = $('.bh-sl-pagination-container .bh-sl-pagination');
+ // Total pages
+ var totalPages = this.totalPages();
+
// Current page check
if (typeof currentPage === 'undefined') {
currentPage = 0;
@@ -707,18 +1408,35 @@
},
/**
- * Marker image setup
- *
+ * Determine if the legacy or Advanced markers should be used
+ *
+ * @returns {boolean}
+ */
+ useLegacyMarkers: function() {
+ this.writeDebug('useLegacyMarkers',arguments);
+
+ return !this.settings.mapSettings.hasOwnProperty('mapId') ||
+ this.settings.mapSettings.mapId === '';
+ },
+
+ /**
+ * Legacy marker image setup
+ *
+ * Original functionality supporting the now deprecated google.maps.Marker
+ * This will eventually be removed in favor of markerImage below.
+ *
* @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) {
+ legacyMarkerImage: function (markerUrl, markerWidth, markerHeight) {
+ this.writeDebug('legacyMarkerImage',arguments);
var markerImg;
-
+
// User defined marker dimensions
- if(typeof markerWidth !== 'undefined' && typeof markerHeight !== 'undefined') {
+ if (typeof markerWidth !== 'undefined' && typeof markerHeight !== 'undefined') {
markerImg = {
url: markerUrl,
size: new google.maps.Size(markerWidth, markerHeight),
@@ -733,7 +1451,41 @@
scaledSize: new google.maps.Size(32, 32)
};
}
-
+
+ return markerImg;
+ },
+
+ /**
+ * Marker image setup
+ *
+ * @param markerUrl {string} path to marker image
+ * @param markerWidth {number} width of marker
+ * @param markerHeight {number} height of marker
+ *
+ * @returns {HTMLImageElement} Image element
+ */
+ markerImage: function (markerUrl, markerWidth, markerHeight) {
+ this.writeDebug('markerImage',arguments);
+
+ // Check if legacy marker image should be used
+ if (this.useLegacyMarkers()) {
+ return this.legacyMarkerImage(markerUrl, markerWidth, markerHeight);
+ }
+
+ var markerImg = document.createElement('img');
+ markerImg.src = markerUrl;
+
+ // User defined marker dimensions
+ if (typeof markerWidth !== 'undefined' && typeof markerHeight !== 'undefined') {
+ markerImg.height = markerHeight;
+ markerImg.width = markerWidth;
+ }
+ // Default marker dimensions: 32px x 32px
+ else {
+ markerImg.height = 32;
+ markerImg.width = 32;
+ }
+
return markerImg;
},
@@ -743,73 +1495,112 @@
* @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 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 = [];
-
- // Remove any spaces from category value
- if(typeof category !== 'undefined' && category.length) {
- category = category.replace(/\s+/g, '');
- }
-
+
// Custom multi-marker image override (different markers for different categories
- if(this.settings.catMarkers !== null) {
- // 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], this.settings.catMarkers[categories[i]][1], this.settings.catMarkers[categories[i]][2]);
+ 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],
+ Number(this.settings.catMarkers[categories[i]][1]),
+ Number(this.settings.catMarkers[categories[i]][2])
+ );
+ }
}
}
- }
- // Single category
- else {
- if(category in this.settings.catMarkers) {
- markerImg = this.markerImage(this.settings.catMarkers[category][0], this.settings.catMarkers[category][1], this.settings.catMarkers[category][2]);
+ // Single category
+ else {
+ if (category in this.settings.catMarkers) {
+ markerImg = this.markerImage(
+ this.settings.catMarkers[category][0],
+ Number(this.settings.catMarkers[category][1]),
+ Number(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);
- }
+ 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
+ );
+ }
}
- // Create the default markers
- if (this.settings.storeLimit === -1 || this.settings.storeLimit > 26 || this.settings.catMarkers !== null || this.settings.markerImg !== null) {
- marker = new google.maps.Marker({
- position : point,
- map : map,
- draggable: false,
- icon: markerImg // Reverts to default marker if nothing is passed
- });
+ // Marker setup
+ if (this.settings.callbackCreateMarker) {
+ // Marker override callback
+ marker = this.settings.callbackCreateMarker.call(this, map, point, letter, category);
}
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
- });
+ // 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))) {
+ if (this.useLegacyMarkers()) {
+ marker = new google.maps.Marker({
+ draggable: false,
+ icon : markerImg, // Reverts to default marker if markerImg not set.
+ map : map,
+ optimized: false,
+ position : point,
+ title : name,
+ });
+ } else {
+ marker = new google.maps.marker.AdvancedMarkerElement({
+ content : markerImg, // Reverts to default marker if markerImg not set.
+ draggable: false,
+ map : map,
+ position : point,
+ title : name,
+ });
+ }
+ }
+ else {
+ // Letter markers
+ if (this.useLegacyMarkers()) {
+ marker = new google.maps.Marker({
+ draggable: false,
+ label : letter,
+ map : map,
+ optimized: false,
+ position : point,
+ title : name,
+ });
+ } else {
+ var letterPin = new google.maps.marker.PinElement({glyph: letter});
+
+ marker = new google.maps.marker.AdvancedMarkerElement({
+ content : letterPin.element,
+ draggable: false,
+ map : map,
+ position : point,
+ title : name,
+ });
+ }
+ }
}
return marker;
@@ -821,37 +1612,60 @@
* @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 (this.useLegacyMarkers()) {
+ this._createLocationVariables(currentMarker.get('id'));
+ } else {
+ this._createLocationVariables(currentMarker.bhslID);
+ }
+
+ var altDistLength,
+ distLength;
+
if (locationData.distance <= 1) {
if (this.settings.lengthUnit === 'km') {
distLength = this.settings.kilometerLang;
+ altDistLength = this.settings.mileLang;
}
else {
distLength = this.settings.mileLang;
+ altDistLength = this.settings.kilometerLang;
}
}
else {
if (this.settings.lengthUnit === 'km') {
distLength = this.settings.kilometersLang;
+ altDistLength = this.settings.milesLang;
}
else {
distLength = this.settings.milesLang;
+ altDistLength = this.settings.kilometersLang;
}
}
+ var markerId;
+
// Set up alpha character
- var markerId = currentMarker.get('id');
+ if (this.useLegacyMarkers()) {
+ markerId = currentMarker.get('id');
+ } else {
+ markerId = currentMarker.bhslID;
+ }
+
// Use dot markers instead of alpha if there are more than 26 locations
- if (this.settings.storeLimit === -1 || this.settings.storeLimit > 26) {
+ 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))) {
+ if (page > 0) {
+ indicator = storeStart + markerId + 1;
+ } else {
indicator = markerId + 1;
- }
- else {
+ }
+ } else {
if (page > 0) {
indicator = String.fromCharCode('A'.charCodeAt(0) + (storeStart + markerId));
}
@@ -863,10 +1677,11 @@
// Define location data
return {
location: [$.extend(locationData, {
- 'markerid': markerId,
- 'marker' : indicator,
- 'length' : distLength,
- 'origin' : addressInput
+ 'markerid' : markerId,
+ 'marker' : indicator,
+ 'altlength': altDistLength,
+ 'length' : distLength,
+ 'origin' : originalOrigin
})]
};
},
@@ -879,17 +1694,47 @@
* @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);
+ $('.' + 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
@@ -897,6 +1742,7 @@
* @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);
@@ -907,52 +1753,130 @@
// Opens the infowindow when list item is clicked
if (location === 'left') {
infowindow.setContent(formattedAddress);
- infowindow.open(marker.get('map'), marker);
+
+ if (this.useLegacyMarkers()) {
+ infowindow.open(marker.get('map'), marker);
+ } else {
+ infowindow.open(marker.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 + ']');
-
- // 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()
+ if (this.useLegacyMarkers()) {
+ 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, locationset[markerId], _this.map);
+ }
+
+ $('.' + _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);
+ }
});
- });
+ } else {
+ marker.addListener('click', function (domEvent, latLng) {
+ infowindow.setContent(formattedAddress);
+ infowindow.open(marker.map, marker);
+
+ // Focus on the list
+ var markerId = marker.bhslID;
+ 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, locationset[markerId], _this.map);
+ }
+
+ $('.' + _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) {
- var _this = this;
- var mappingObj = {};
+ 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) {
- var originAddress = data.address;
- mappingObj.lat = position.coords.latitude;
- mappingObj.lng = position.coords.longitude;
+ 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);
+
+ // Fill in the search box.
+ if (typeof originAddress !== 'undefined') {
+ $('#' + _this.settings.addressID).val(originAddress);
+ }
} else {
// Unable to geocode
_this.notify(_this.settings.addressErrorAlert);
@@ -965,16 +1889,81 @@
*
*/
_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);
+
+ var currentUrl = window.location.href;
+ var url = new URL(currentUrl);
+
+ // Update the page in the URL.
+ url.searchParams.set('bhsl-page', parseInt(newPage) + 1);
+
+ // Update the query string param to match the new value.
+ if (history.pushState) {
+ window.history.pushState({path: url.href}, '', url.href);
+ }
// Page change callback
if (this.settings.callbackPageChange) {
@@ -987,12 +1976,14 @@
/**
* Get the address by marker ID
- *
+ *
* @param markerID {number} location ID
+ *
* @returns {string} formatted address
*/
getAddressByMarker: function(markerID) {
- var formattedAddress = null;
+ 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 + ' '; }
@@ -1000,7 +1991,7 @@
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;
},
@@ -1008,33 +1999,53 @@
* Clear the markers from the map
*/
clearMarkers: function() {
- for (var i = 0; i < locationset.length; i++) {
+ 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);
+ this.settings.callbackDirectionsRequest.call(this, origin, locID, map, locationset[locID]);
}
-
+
var destination = this.getAddressByMarker(locID);
- if(destination) {
+ 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));
@@ -1060,18 +2071,16 @@
* 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.settings.locationList + ' .adp').remove();
- $('.' + this.settings.locationList + ' ul').fadeIn();
-
this.reset();
-
+
if ((olat) && (olng)) {
if (this.countFilters() === 0) {
this.settings.mapSettings.zoom = originalZoom;
@@ -1085,113 +2094,367 @@
$(document).off('click.'+pluginName, '.' + this.settings.locationList + ' .bh-sl-close-icon');
},
+ /**
+ * Handle length unit swap
+ *
+ * @param $lengthSwap
+ */
+ lengthUnitSwap: function($lengthSwap) {
+ this.writeDebug('lengthUnitSwap',arguments);
+
+ if ($lengthSwap.val() === 'alt-distance') {
+ $('.' + this.settings.locationList + ' .loc-alt-dist').show();
+ $('.' + this.settings.locationList + ' .loc-default-dist').hide();
+ } else if ($lengthSwap.val() === 'default-distance') {
+ $('.' + this.settings.locationList + ' .loc-default-dist').show();
+ $('.' + this.settings.locationList + ' .loc-alt-dist').hide();
+ }
+ },
+
/**
* Process the form values and/or query string
*
* @param e {Object} event
*/
processForm: function (e) {
- var _this = this;
- var distance = null;
-
- // Stop the form submission
- if(typeof e !== 'undefined' && e !== null) {
+ this.writeDebug('processForm',arguments);
+ var _this = this,
+ distance = null,
+ geocodeRestrictions = {},
+ $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();
}
- // Get the distance if set
- if (this.settings.maxDistance === true) {
- distance = $('#' + this.settings.maxDistanceID).val();
- }
-
- if(this.settings.querystringParams === true) {
+ // Blur any form field to hide mobile keyboards.
+ $('.' + _this.settings.formContainer +' input, .' + _this.settings.formContainer + ' select').blur();
+ // Query string parameters
+ if (this.settings.querystringParams === true) {
// Check for query string parameters
- if(this.getQueryString(this.settings.addressID) || this.getQueryString(this.settings.searchID)){
+ 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);
- }
- else{
+ distance = this.getQueryString(this.settings.maxDistanceID);
+
+ // Max distance field.
+ if (distance && $('#' + this.settings.maxDistanceID + ' option[value=' + distance + ']').length) {
+ $distanceInput.val(distance);
+ }
+
+ // Update zoom if origin coordinates are available and a distance query string value is set.
+ if (addressInput && distance) {
+ _this.settings.mapSettings.zoom = 0;
+ }
+
+ // 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 = $('#' + this.settings.addressID).val();
- searchInput = $('#' + this.settings.searchID).val();
+ 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 = $('#' + this.settings.addressID).val();
- searchInput = $('#' + this.settings.searchID).val();
+ 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();
+ }
+
+ // Form values callback
+ if (this.settings.callbackFormVals) {
+ this.settings.callbackFormVals.call(this, addressInput, searchInput, distance, region);
+ }
+
+ // Add component restriction if the region has been set.
+ if (typeof region !== 'undefined') {
+ geocodeRestrictions = {
+ country: region
+ };
}
- // Get the region setting if set
- var region = $('#' + this.settings.regionID).val();
+ // Component restriction value via callback.
+ if (typeof this.settings.callbackGeocodeRestrictions === 'function') {
+ // Component restriction override callback
+ geocodeRestrictions = this.settings.callbackGeocodeRestrictions.call(this, addressInput, searchInput, distance);
+ }
- if (addressInput === '' && searchInput === '') {
+ if (addressInput === '' && searchInput === '' && this.settings.autoGeocode !== true) {
this._start();
}
- else if(addressInput !== '') {
- 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;
- _this.mapping(mappingObj);
- } else {
- // Unable to geocode
- _this.notify(_this.settings.addressErrorAlert);
- }
- });
+ else if (addressInput !== '') {
+ // Check for existing name search and remove if address input is blank.
+ if (searchInput === '' && filters.hasOwnProperty('name')) {
+ delete filters.name;
+ }
+
+ // 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,
+ componentRestrictions: geocodeRestrictions,
+ 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);
}
- else if(searchInput !== '') {
+ else if (this.settings.autoGeocode === true) {
+ // Run the mapping function
+ mappingObj.lat = olat;
+ mappingObj.lng = olng;
+ mappingObj.origin = addressInput;
mappingObj.name = searchInput;
+ mappingObj.distance = distance;
_this.mapping(mappingObj);
}
+
+ // Reset pagination if the input has changed.
+ if (typeof originalOrigin !== 'undefined' && addressInput !== originalOrigin) {
+ this.paginationReset();
+ }
},
/**
- * Checks distance of each location and setups up the locationset array
- *
+ * 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 firstRun {boolean} initial load check
* @param origin {string} origin address
* @param maxDistance {number} maximum distance if set
*/
- locationsSetup: function (data, lat, lng, firstRun, origin, maxDistance) {
+ 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);
+
+ // Alternative distance length unit
+ if (this.settings.lengthUnit === 'm') {
+ // Miles to kilometers
+ data.altdistance = parseFloat(data.distance)*1.609344;
+ } else if (this.settings.lengthUnit === 'km') {
+ // Kilometers to miles
+ data.altdistance = parseFloat(data.distance)/1.609344;
+ }
}
}
+ // Make sure the location coordinates are valid.
+ if (!this.coordinatesInRange(data.lat, data.lng)) {
+ this.writeDebug('locationsSetup', "location ignored because coordinates out of range: " + maxDistance, data);
+ return;
+ }
+
// Create the array
- if (this.settings.maxDistance === true && firstRun !== true && maxDistance !== null) {
- if (data.distance < maxDistance) {
+ if (this.settings.maxDistance === true && typeof maxDistance !== 'undefined' && maxDistance !== null) {
+ if (data.distance <= maxDistance) {
locationset.push( data );
+ } else {
+ this.writeDebug('locationsSetup', "location ignored because it is out of maxDistance: " + maxDistance, data);
+ return;
}
- else {
+ } else if (this.settings.maxDistance === true && this.settings.querystringParams === true && typeof maxDistance !== 'undefined' && maxDistance !== null) {
+ if (data.distance <= maxDistance) {
+ locationset.push( data );
+ } else {
+ this.writeDebug('locationsSetup', "location ignored because it is out of maxDistance: " + maxDistance, data);
return;
}
- }
- else {
+ } else {
locationset.push( data );
}
},
+ /**
+ * Set up front-end sorting functionality
+ */
+ sorting: function() {
+ this.writeDebug('sorting',arguments);
+ var _this = this,
+ $mapDiv = $('#' + _this.settings.mapID),
+ $sortSelect = $('#' + _this.settings.sortID);
+
+ if ($sortSelect.length === 0) {
+ return;
+ }
+
+ $sortSelect.on('change.'+pluginName, function (e) {
+ e.stopPropagation();
+
+ // Reset pagination.
+ if (_this.settings.pagination === true) {
+ _this.paginationChange(0);
+ }
+
+ var sortMethod,
+ sortVal;
+
+ sortMethod = (typeof $(this).find(':selected').attr('data-method') !== 'undefined') ? $(this).find(':selected').attr('data-method') : 'distance';
+ sortVal = $(this).val();
+
+ _this.settings.sortBy.method = sortMethod;
+ _this.settings.sortBy.prop = sortVal;
+
+ // Callback
+ if (_this.settings.callbackSorting) {
+ _this.settings.callbackSorting.call(this, _this.settings.sortBy);
+ }
+
+ if ($mapDiv.hasClass('bh-sl-map-open')) {
+ _this.mapping(mappingObj);
+ }
+ });
+ },
+
+ /**
+ * Set up front-end ordering functionality - this ties in to sorting and that has to be enabled for this to
+ * work.
+ */
+ order: function() {
+ this.writeDebug('order',arguments);
+ var _this = this,
+ $mapDiv = $('#' + _this.settings.mapID),
+ $orderSelect = $('#' + _this.settings.orderID);
+
+ if ($orderSelect.length === 0) {
+ return;
+ }
+
+ $orderSelect.on('change.'+pluginName, function (e) {
+ e.stopPropagation();
+
+ // Reset pagination.
+ if (_this.settings.pagination === true) {
+ _this.paginationChange(0);
+ }
+
+ _this.settings.sortBy.order = $(this).val();
+
+ // Callback
+ if (_this.settings.callbackOrder) {
+ _this.settings.callbackOrder.call(this, _this.settings.order);
+ }
+
+ if ($mapDiv.hasClass('bh-sl-map-open')) {
+ _this.mapping(mappingObj);
+ }
+ });
+ },
+
+ /**
+ * Distance filtering
+ */
+ distanceFiltering: function () {
+ this.writeDebug('distanceFiltering');
+ var _this = this;
+ var $distanceInput = $('#' + this.settings.maxDistanceID);
+
+ // Add event listener
+ $distanceInput.on('change.'+pluginName, function (e) {
+ e.stopPropagation();
+
+ // Query string parameter value updates on change.
+ if (_this.settings.querystringParams === true) {
+ var currentUrl = window.location.href;
+ var url = new URL(currentUrl);
+
+ // Update the distance in the URL.
+ url.searchParams.set(_this.settings.maxDistanceID, this.value);
+
+ // Update the query string param to match the new value.
+ if (history.pushState) {
+ window.history.pushState({path: url.href}, '', url.href);
+ } else {
+ window.location.replace(url.href);
+ }
+ }
+
+ if ($('#' + _this.settings.mapID).hasClass('bh-sl-map-open') === true) {
+ if ((olat) && (olng)) {
+ _this.settings.mapSettings.zoom = 0;
+ _this.processForm();
+ } else {
+ _this.mapping(mappingObj);
+ }
+ }
+ });
+ },
+
/**
* Count the selected filters
*
* @returns {number}
*/
countFilters: function () {
+ this.writeDebug('countFilters');
var filterCount = 0;
if (!this.isEmptyObject(filters)) {
@@ -1211,6 +2474,7 @@
* @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();
@@ -1229,9 +2493,10 @@
* @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];
@@ -1241,10 +2506,11 @@
/**
* 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();
@@ -1259,29 +2525,96 @@
/**
* Check for existing filter selections
- *
*/
checkFilters: function () {
+ this.writeDebug('checkFilters');
for(var key in this.settings.taxonomyFilters) {
- if(this.settings.taxonomyFilters.hasOwnProperty(key)) {
+
+ 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);
}
}
},
+ /**
+ * Select the indicated values from query string parameters.
+ *
+ * @param taxonomy {string} Current taxonomy.
+ * @param value {array} Query string array values.
+ */
+ selectQueryStringFilters: function( taxonomy, value ) {
+ this.writeDebug('selectQueryStringFilters', arguments);
+
+ var $taxGroupContainer = $('#' + this.settings.taxonomyFilters[taxonomy]);
+
+ // Handle checkboxes.
+ if ( $taxGroupContainer.find('input[type="checkbox"]').length ) {
+
+ for ( var i = 0; i < value.length; i++ ) {
+ $taxGroupContainer.find('input:checkbox[value="' + value[i] + '"]').prop('checked', true);
+ }
+ }
+
+ // Handle select fields.
+ if ( $taxGroupContainer.find('select').length ) {
+ // Only expecting one value for select fields.
+ $taxGroupContainer.find('option[value="' + value[0] + '"]').prop('selected', true);
+ }
+
+ // Handle radio buttons.
+ if ( $taxGroupContainer.find('input[type="radio"]').length ) {
+ // Only one value for radio button.
+ $taxGroupContainer.find('input:radio[value="' + value[0] + '"]').prop('checked', true);
+ }
+ },
+
+ /**
+ * 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);
+
+ // Check for multiple values separated by comma.
+ if ( filterVal.indexOf( ',' ) !== -1 ) {
+ filterVal = filterVal.split( ',' );
+ }
+
+ // Only add the taxonomy id if it doesn't already exist
+ if (typeof filterVal !== 'undefined' && filterVal !== '' && filters[key].indexOf(filterVal) === -1) {
+ if ( Array.isArray( filterVal ) ) {
+ filters[key] = filterVal;
+ } else {
+ filters[key] = [filterVal];
+ }
+ }
+
+ // Select the filters indicated in the query string.
+ if ( filters[key].length ) {
+ this.selectQueryStringFilters( key, filters[key] );
+ }
+ }
+ }
+ },
+
/**
* 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++) {
@@ -1294,17 +2627,31 @@
},
/**
- * Taxonomy filtering
+ * Initialize or reset the filters object to its original state
*/
- taxonomyFiltering: function() {
- var _this = this;
+ taxonomyFiltersInit: function () {
+ this.writeDebug('taxonomyFiltersInit');
// Set up the filters
for(var key in this.settings.taxonomyFilters) {
- if(this.settings.taxonomyFilters.hasOwnProperty(key)) {
+ 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) {
@@ -1312,6 +2659,11 @@
var filterVal, filterContainer, filterKey;
+ // Reset pagination.
+ if (_this.settings.pagination === true) {
+ _this.paginationReset();
+ }
+
// Handle checkbox filters
if ($(this).is('input[type="checkbox"]')) {
// First check for existing selections
@@ -1325,10 +2677,10 @@
// 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) {
+ 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;
@@ -1348,10 +2700,10 @@
if ((olat) && (olng)) {
if (_this.countFilters() === 0) {
_this.settings.mapSettings.zoom = originalZoom;
- }
- else {
+ } else {
_this.settings.mapSettings.zoom = 0;
}
+
_this.processForm();
}
else {
@@ -1379,8 +2731,7 @@
if ((olat) && (olng)) {
_this.settings.mapSettings.zoom = 0;
_this.processForm();
- }
- else {
+ } else {
_this.mapping(mappingObj);
}
}
@@ -1405,460 +2756,1247 @@
},
/**
- * The primary mapping function that runs everything
- *
- * @param mappingObject {Object} all the potential mapping properties - latitude, longitude, origin, name, max distance, page
+ * Updates the location list to reflect the markers that are displayed on the map
+ *
+ * @param markers {Object} Map markers
+ * @param map {Object} Google map
*/
- mapping: function (mappingObject) {
+ checkVisibleMarkers: function(markers, map) {
+ this.writeDebug('checkVisibleMarkers',arguments);
var _this = this;
- var orig_lat, orig_lng, origin, name, maxDistance, page, firstRun, marker, bounds, storeStart, storeNumToShow, myOptions, noResults;
- var i = 0;
- if (!this.isEmptyObject(mappingObject)) {
- orig_lat = mappingObject.lat;
- orig_lng = mappingObject.lng;
- origin = mappingObject.origin;
- name = mappingObject.name;
- maxDistance = mappingObject.distance;
- page = mappingObject.page;
- }
-
- // Enable the visual refresh https://developers.google.com/maps/documentation/javascript/basics#VisualRefresh
- google.maps.visualRefresh = true;
+ var locations, listHtml;
- // Set the initial page to zero if not set
- if (typeof page === 'undefined') {
- page = 0;
- }
-
- // Data request
- if (typeof origin === 'undefined' && this.settings.nameSearch === true) {
- dataRequest = _this._getData();
- }
- else {
- // Setup the origin point
- var 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;
+ // Empty the location list
+ $('.' + this.settings.locationList + ' ul').empty();
+
+ // Set up the new list
+ $(markers).each(function(x, marker){
+ if (_this.useLegacyMarkers()) {
+ 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);
+ }
+ } else {
+ if (map.getBounds().contains(marker.position)) {
+ // 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);
+ }
}
- 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);
+ });
+
+ // 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 address
+ 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);
}
-
- // Check filters here to handle selected filtering after page reload
- if(_this.settings.taxonomyFilters !== null && _this.hasEmptyObjectVals(filters)) {
- _this.checkFilters();
+
+ // Empty the location list
+ locList.empty();
+
+ // Append the no results message
+ noResults = $('' + this.settings.noResultsTitle + '
' + this.settings.noResultsDesc + '').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);
}
- /**
- * Process the location data
- */
- dataRequest.done(function (data) {
- var $mapDiv = $('#' + _this.settings.mapID);
- // Get the length unit
- var distUnit = (_this.settings.lengthUnit === 'km') ? _this.settings.kilometersLang : _this.settings.milesLang;
+ this.map.setCenter(center);
- // Save data and origin separately so we can potentially avoid multiple AJAX requests
- originalData = dataRequest;
- originalOrigin = origin;
-
- // Callback
- if (_this.settings.callbackSuccess) {
- _this.settings.callbackSuccess.call(this);
- }
-
- // Set a variable for fullMapStart so we can detect the first run
- if (_this.settings.fullMapStart === true && $mapDiv.hasClass('bh-sl-map-open') === false) {
- firstRun = true;
- }
- else {
- _this.reset();
- }
+ if (originalZoom) {
+ this.map.setZoom(originalZoom);
+ }
+ },
- $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];
- }
- }
+ /**
+ * 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;
+ }
- _this.locationsSetup(locationData, orig_lat, orig_lng, firstRun, origin, maxDistance);
+ var marker,
+ originImg;
- i++;
+ if (typeof origin !== 'undefined') {
+ if (this.useLegacyMarkers()) {
+ 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 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()
+ else {
+ originImg = {
+ url: 'https://mt.googleapis.com/vt/icon/name=icons/spotlight/spotlight-waypoint-a.png'
};
+ }
- _this.locationsSetup(locationData, orig_lat, orig_lng, firstRun, origin, maxDistance);
+ marker = new google.maps.Marker({
+ position : originPoint,
+ map : map,
+ icon : originImg,
+ draggable: false
+ });
+ } else {
+ // Default green origin pin.
+ var defaultOriginPin = new google.maps.marker.PinElement({
+ background : '#39b25e',
+ borderColor: '#177d3d',
+ glyphColor : '#177d3c'
+ });
- i++;
+ marker = new google.maps.marker.AdvancedMarkerElement({
+ content : defaultOriginPin.element,
+ draggable: false,
+ map : map,
+ position : originPoint,
+ title : name,
});
- }
- 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;
- }
+ // Origin image.
+ if (this.settings.originMarkerImg !== null) {
+ originImg = document.createElement('img');
+
+ if (this.settings.originMarkerDim === null) {
+ originImg.src = this.settings.originMarkerImg;
+ } else {
+ originImg = this.markerImage(
+ this.settings.originMarkerImg,
+ this.settings.originMarkerDim.width,
+ this.settings.originMarkerDim.height,
+ );
}
- _this.locationsSetup(locationData, orig_lat, orig_lng, firstRun, origin, maxDistance);
+ marker.content = originImg;
+ }
+ }
+ }
+ },
- i++;
- });
+ /**
+ * 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();
}
-
- // 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) {
- var taxFilters = {};
-
- 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(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1") + '\\b)';
- }
- }
- }
- // Filter the data
- if (!_this.isEmptyObject(taxFilters)) {
- locationset = $.grep(locationset, function (val) {
- return _this.filterData(val, taxFilters);
- });
- }
+ });
+ },
+
+ /**
+ * Open and select the location closest to the origin
+ *
+ * @param nearestLoc {Object} Details for the nearest location
+ * @param infowindow {Object} Info window object
+ * @param storeStart {number} Starting point of current page when pagination is enabled
+ * @param page {number} Current page number when pagination is enabled
+ */
+ openNearestLocation: function(nearestLoc, infowindow, storeStart, page) {
+ this.writeDebug('openNearestLocation',arguments);
+
+ if (
+ this.settings.openNearest !== true ||
+ typeof nearestLoc === 'undefined' ||
+ typeof originalOrigin === 'undefined' ||
+ (this.settings.fullMapStart === true && firstRun === true && this.settings.querystringParams === false) ||
+ (this.settings.defaultLoc === true && firstRun === true && this.settings.querystringParams === false)
+ ) {
+ return;
+ }
+
+ var _this = this;
+
+ // Callback
+ if (_this.settings.callbackNearestLoc) {
+ _this.settings.callbackNearestLoc.call(this, _this.map, nearestLoc, infowindow, storeStart, page);
+ }
+
+ var markerId = (nearestLoc.hasOwnProperty('markerid')) ? nearestLoc.markerid : 0;
+ var selectedMarker = markers[markerId];
+
+ _this.createInfowindow(selectedMarker, 'left', infowindow, storeStart, page);
+
+ // Scroll list to selected marker
+ var $container = $('.' + _this.settings.locationList);
+ var $selectedLocation = $('.' + _this.settings.locationList + ' li[data-markerid=' + markerId + ']');
+
+ // Focus on the list
+ $('.' + _this.settings.locationList + ' li').removeClass('list-focus');
+ $selectedLocation.addClass('list-focus');
+
+ $container.animate({
+ scrollTop: $selectedLocation.offset().top - $container.offset().top + $container.scrollTop()
+ });
+ },
+
+ /**
+ * Handle clicks from the location list
+ *
+ * @param map {Object} Google map
+ * @param infowindow {Object} Info window object
+ * @param storeStart {number} Starting point of current page when pagination is enabled
+ * @param page {number} Current page number when pagination is enabled
+ */
+ 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, locationset[markerId], map);
}
- // Handle no results
- if (_this.isEmptyObject(locationset)) {
- // Callback
- if (_this.settings.callbackNoResults) {
- _this.settings.callbackNoResults.call(this);
- }
-
- // Hide the map and locations if they're showing
- if ($mapDiv.hasClass('bh-sl-map-open')) {
- $this.hide();
- }
-
- // Append the no results message
- noResults = $('
' + _this.settings.noResultsTitle + '
' + _this.settings.noResultsDesc + '').hide().fadeIn();
-
- // Setup a no results location
- locationset[0] = {
- 'distance': 0,
- 'lat' : 0,
- 'lng': 0
- };
+ if (_this.useLegacyMarkers()) {
+ map.panTo(selectedMarker.getPosition());
+ } else {
+ map.panTo(selectedMarker.position);
}
- // Sort the multi-dimensional array by distance
- if (typeof origin !== 'undefined') {
- _this.sortNumerically(locationset);
+ var listLoc = 'left';
+ _this.createInfowindow(selectedMarker, listLoc, infowindow, storeStart, page);
+
+ // Custom selected marker override
+ if (_this.settings.selectedMarkerImg !== null) {
+ _this.changeSelectedMarker(selectedMarker);
}
- // Featured locations filtering
- if (_this.settings.featuredLocations === true) {
- // Create array for featured locations
- featuredset = $.grep(locationset, function (val) {
- return val.featured === 'true';
- });
+ // Focus on the list
+ $('.' + _this.settings.locationList + ' li').removeClass('list-focus');
+ $('.' + _this.settings.locationList + ' li[data-markerid=' + markerId + ']').addClass('list-focus');
+ });
- // Create array for normal locations
- normalset = $.grep(locationset, function (val) {
- return val.featured !== 'true';
- });
+ // 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, parseInt(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);
+ });
+ },
+
+ /**
+ * Restrict featured locations from displaying in results by a specific distance
+ *
+ * @returns {Array}
+ */
+ featuredDistanceRestriction: function() {
+ this.writeDebug('featuredDistanceRestriction',arguments);
+ var _this = this;
+
+ featuredset = $.grep(featuredset, function (val) {
- // Combine the arrays
- locationset = [];
- locationset = featuredset.concat(normalset);
+ if (val.hasOwnProperty('distance')) {
+ return parseFloat(val.distance) <= parseFloat(_this.settings.featuredDistance);
}
+ });
- // Check the closest marker
- if (_this.settings.maxDistance === true && firstRun !== true && maxDistance) {
- if (typeof locationset[0] === 'undefined' || locationset[0].distance > maxDistance) {
- _this.notify(_this.settings.distanceErrorAlert + maxDistance + ' ' + distUnit);
- return;
- }
+ return featuredset;
+ },
+
+ /**
+ * Restrict featured locations by distance.
+ *
+ * @returns {Array}
+ */
+ featuredRestrictions: function(mappingObject) {
+ this.writeDebug('featuredRestrictions',arguments);
+
+ if (this.settings.featuredDistance === null) {
+ return featuredset;
+ }
+
+ // Featured locations radius restriction.
+ if (this.settings.featuredDistance !== null) {
+ featuredset = this.featuredDistanceRestriction(mappingObject);
+ }
+
+ return featuredset;
+ },
+
+ /**
+ * 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;
+ }
+
+ paginationPage = page;
+ }
+
+ // Override page if the query string was set.
+ var queryStringPage = _this.getQueryString('bhsl-page');
+ if (queryStringPage !== '') {
+ page = paginationPage = parseInt(queryStringPage) - 1;
+ }
+
+ // Data request
+ if (typeof origin === 'undefined' && this.settings.nameSearch === true) {
+ dataRequest = _this._getData();
+ }
+ else {
+ // Set up 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') && this.settings.pagination !== true) {
+ origin = originalOrigin;
+ dataRequest = originalData;
}
else {
- if (_this.settings.distanceAlert !== -1 && locationset[0].distance > _this.settings.distanceAlert) {
- _this.notify(_this.settings.distanceErrorAlert + _this.settings.distanceAlert + ' ' + distUnit);
- }
+ // 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, mappingObject);
}
+ }
- // Output page numbers if pagination setting is true
- if (_this.settings.pagination === true) {
- _this.paginationSetup(page);
- }
+ // 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);
+ });
+ }
+ },
- // Slide in the map container
- if (_this.settings.slideMap === true) {
- $this.slideDown();
- }
-
- // Set up the modal window
- if (_this.settings.modal === true) {
- // Callback
- if (_this.settings.callbackModalOpen) {
- _this.settings.callbackModalOpen.call(this);
+ /**
+ * Reset disabled form fields
+ */
+ resetDisabledFilterVals: function() {
+ this.writeDebug('resetDisabledFilterVals');
+
+ for (var taxKey in this.settings.taxonomyFilters) {
+ if (this.settings.taxonomyFilters.hasOwnProperty(taxKey)) {
+ for (var x = 0; x < this.settings.taxonomyFilters[taxKey].length; x++) {
+ $('#' + this.settings.taxonomyFilters[taxKey] + ' input,option').each(function () {
+ var disabled = $(this).attr('disabled');
+
+ if (typeof disabled !== 'undefined') {
+ $(this).removeAttr('disabled');
+ }
+ });
}
+ }
+ }
+ },
- // 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();
+ /**
+ * Get available filter values
+ *
+ * @param callback
+ */
+ getAvailableFilters: function(callback) {
+ this.writeDebug('getAvailableFilters');
+ var availableValues = [];
+
+ for (var location in locationset) {
+ if (locationset.hasOwnProperty(location)) {
+ // Loop through the location values.
+ for (var locationKey in locationset[location]) {
+ if (filters.hasOwnProperty(locationKey) && locationset[location][locationKey] !== '') {
+ if (availableValues.hasOwnProperty(locationKey)) {
+ var availableVal = availableValues[locationKey].concat(',', locationset[location][locationKey].replace(', ', ',').trim());
+ availableValues[locationKey] = Array.from(new Set(availableVal.split(','))).toString();
+ } else {
+ availableValues[locationKey] = locationset[location][locationKey].replace(', ', ',').trim();
+ }
}
- });
+ }
}
+ }
- // Avoid error if number of locations is less than the default of 26
- if (_this.settings.storeLimit === -1 || locationset.length < _this.settings.storeLimit) {
- storeNum = locationset.length;
+ // Account for missing filter properties in location set.
+ for (var keyName in filters) {
+ if (!availableValues.hasOwnProperty(keyName)) {
+ availableValues[keyName] = '';
}
- else {
- storeNum = _this.settings.storeLimit;
+ }
+
+ callback(availableValues);
+ },
+
+ /**
+ * Disable input fields that aren't available within the current location set
+ */
+ maybeDisableFilterOptions: function() {
+ this.writeDebug('maybeDisableFilterOptions');
+ var availableValues = [];
+ var _this = this;
+
+ // Initially reset any input/option fields that were previously disabled.
+ this.resetDisabledFilterVals();
+
+ // Loop through current location set to determine what filter values are still available.
+ this.getAvailableFilters( function(values) {
+ availableValues = values;
+
+ // Save the original filter values for reference.
+ if (typeof originalFilterVals === 'undefined') {
+ originalFilterVals = availableValues;
}
- // 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;
+ // Update input and option fields to disabled if they're not available.
+ for (var key in _this.settings.taxonomyFilters) {
+ if (_this.settings.taxonomyFilters.hasOwnProperty(key)) {
+
+ // Loop through the taxonomy filter group items.
+ for (var i = 0; i < _this.settings.taxonomyFilters[key].length; i++) {
+ if (_this.settings.taxonomyFilters.hasOwnProperty(key)) {
+ $('#' + _this.settings.taxonomyFilters[key] + ' input, #' + _this.settings.taxonomyFilters[key] + ' option').each(function () {
- if( (storeStart + storeNumToShow) > locationset.length ) {
- storeNumToShow = _this.settings.locationsPerPage - ((storeStart + storeNumToShow) - locationset.length);
+ // Initial determination of values that should be disabled.
+ if ($(this).val() !== '' && ! Array.from(new Set(availableValues[key].split(','))).includes($(this).val())) {
+ if (! disabledFilterVals.hasOwnProperty(key)) {
+ disabledFilterVals[key] = [];
+ }
+
+ // Handle select options and radio button values when there is no address input.
+ if (
+ (typeof addressInput === 'undefined' || addressInput === '') &&
+ ($(this).prop('tagName') === 'OPTION' || $(this).prop('type') === 'radio') &&
+ _this.hasSingleGroupFilterVal(filters, key) &&
+ Array.from(new Set(originalFilterVals[key].split(','))).includes($(this).val())
+ ) {
+ return;
+ }
+
+ // Handle select options and radio button values when there is address input.
+ if (
+ (typeof addressInput !== 'undefined' || addressInput !== '') &&
+ ($(this).prop('tagName') === 'OPTION' || $(this).prop('type') === 'radio') &&
+ _this.hasSingleGroupFilterVal(filters, key) &&
+ Array.from(new Set(originalFilterVals[key].split(','))).includes($(this).val()) &&
+ _this.countFilters() === 1
+ ) {
+ return;
+ }
+
+ // Keep select options and radio button available values after one filter has been selected.
+ if (
+ ($(this).prop('tagName') === 'OPTION' || $(this).prop('type') === 'radio') &&
+ _this.hasSingleGroupFilterVal(filters, key) &&
+ _this.countFilters() > 1 &&
+ Array.from(new Set(originalFilterVals[key].split(','))).includes($(this).val()) &&
+ ! disabledFilterVals[key].includes($(this).val())
+ ) {
+ return;
+ }
+
+ // Track disabled values.
+ if (
+ disabledFilterVals.hasOwnProperty(key) &&
+ Array.isArray(disabledFilterVals[key]) &&
+ ! disabledFilterVals[key].includes($(this).val())
+ ) {
+ disabledFilterVals[key].push($(this).val());
+ }
+
+ $(this).attr('disabled', true);
+ }
+ });
+ }
+ }
}
-
- locationset = locationset.slice(storeStart, storeStart + storeNumToShow);
- storeNum = locationset.length;
- }
- else {
- storeNumToShow = storeNum;
- storeStart = 0;
}
+ });
+ },
- // Google maps settings
- if ((_this.settings.fullMapStart === true && firstRun === true) || (_this.settings.mapSettings.zoom === 0) || (typeof origin === 'undefined')) {
- myOptions = _this.settings.mapSettings;
- bounds = new google.maps.LatLngBounds();
+ /**
+ * 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, nearestLoc;
+ var taxFilters = {};
+ var $lengthSwap = $('#' + _this.settings.lengthSwapID);
+
+ 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, mappingObject, originPoint, data, page);
+ }
+
+ 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 if (
+ (_this.settings.fullMapStart === true && reload === true) ||
+ (_this.settings.autoGeocode === true && reload === true) ||
+ (_this.settings.defaultLoc === true && reload === true)
+ ) {
+ _this.reset();
+ } 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.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);
+ }
+ 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 = {};
- if (page === 0) {
- _this.settings.mapSettings.center = originPoint;
- myOptions = _this.settings.mapSettings;
+ for (var key in this.attributes) {
+ if (this.attributes.hasOwnProperty(key)) {
+ locationData[this.attributes[key].name] = this.attributes[key].value;
+ }
}
- else {
- _this.settings.mapSettings.center = nextPoint;
- myOptions = _this.settings.mapSettings;
+
+ _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' && '' !== searchInput) {
+
+ if (_this.settings.nameAttribute.indexOf(',')) {
+ nameAttrs = _this.settings.nameAttribute.split(',');
+
+ // Multiple name attributes should swap to exclusive filtering.
+ if (_this.settings.exclusiveTax !== null) {
+ _this.settings.exclusiveTax.concat(nameAttrs);
+ } else {
+ _this.settings.exclusiveTax = nameAttrs;
+ }
+
+ for (var a = 0; a < nameAttrs.length; a++) {
+ filters[nameAttrs[a].trim()] = [searchInput];
+ }
+ } else {
+ filters[_this.settings.nameAttribute] = [searchInput];
}
}
- else {
- _this.settings.mapSettings.center = originPoint;
- myOptions = _this.settings.mapSettings;
+
+ // Check for a previous value.
+ if (
+ typeof searchInput !== 'undefined' &&
+ '' === searchInput
+ ) {
+ if (typeof nameAttrs !== 'undefined') {
+ for (var pa = 0; pa < nameAttrs.length; pa++) {
+ if (nameAttrs[pa] in filters) {
+ delete filters[nameAttrs[pa]];
+ }
+ }
+ } else {
+ delete filters[_this.settings.nameAttribute];
+ }
}
+ }
- // Create the map
- var map = new google.maps.Map(document.getElementById(_this.settings.mapID), myOptions);
+ // Taxonomy filtering setup
+ if (_this.settings.taxonomyFilters !== null || _this.settings.nameSearch === true) {
- // Re-center the map when the browser is resized
- google.maps.event.addDomListener(window, 'resize', function() {
- var center = map.getCenter();
- google.maps.event.trigger(map, 'resize');
- map.setCenter(center);
- });
-
- // Load the map
- $this.data(_this.settings.mapID.replace('#', ''), map);
-
- // Initialize the infowondow
- var infowindow = new google.maps.InfoWindow();
-
- // Add origin marker if the setting is set
- if (_this.settings.originMarker === true) {
- var originImg = '';
-
- // If fullMapStart is on and it's the first run there is no origin
- if(_this.settings.fullMapStart === false && firstRun === true) {
- return;
- }
- else{
- if(_this.settings.originMarkerImg !== null) {
- if(_this.settings.originMarkerDim === null) {
- originImg = _this.markerImage(_this.settings.originMarkerImg);
+ 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] = [];
}
- else {
- originImg = _this.markerImage(_this.settings.originMarkerImg, _this.settings.originMarkerDim.width, _this.settings.originMarkerDim.height);
+
+ // Swap pattern matching depending on name search vs. taxonomy filtering.
+ if (typeof nameAttrs !== 'undefined') {
+ if (nameAttrs.indexOf(k) !== -1) {
+ taxFilters[k][z] = '(?:^|\\s)' + filters[k][z].replace(/([.*+?^=!:${}()|\[\]\/\\]|&\s+)/g, '');
+ } else {
+ taxFilters[k][z] = '(?=.*' + filters[k][z].replace(/([.*+?^=!:${}()|\[\]\/\\]|&\s+)/g, '') + '(?!\\s))';
+ }
+ } else {
+ if (k === _this.settings.nameAttribute) {
+ taxFilters[k][z] = '(?:^|\\s)' + filters[k][z].replace(/([.*+?^=!:${}()|\[\]\/\\]|&\s+)/g, '');
+ } else {
+ taxFilters[k][z] = '(?=.*' + filters[k][z].replace(/([.*+?^=!:${}()|\[\]\/\\]|&\s+)/g, '') + '(?!\\s))';
+ }
}
}
- 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
- });
}
}
- // Handle pagination
- $(document).on('click.'+pluginName, '.bh-sl-pagination li', function () {
- // Run paginationChange
- _this.paginationChange($(this).attr('data-page'));
- });
-
- // Inline directions
- if(_this.settings.inlineDirections === true){
- // 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);
+ // Filter the data
+ if (!_this.isEmptyObject(taxFilters)) {
+ locationset = $.grep(locationset, function (val) {
+ return _this.filterData(val, taxFilters);
});
+ }
+ }
- // Close directions
- $(document).on('click.'+pluginName, '.' + _this.settings.locationList + ' .bh-sl-close-icon', function () {
- _this.closeDirections();
- });
+ // Sorting
+ if (_this.settings.sortBy !== null && typeof _this.settings.sortBy === 'object') {
+
+ // Sort the multi-dimensional array by distance to get the nearest location first when enabled
+ if (_this.settings.openNearest === true && typeof originalOrigin !== 'undefined') {
+ this.sortNumerically(locationset, true);
+
+ // Save the closest location to a variable for openNearest setting
+ if (typeof locationset[0] !== 'undefined') {
+
+ if (this.settings.sortBy.hasOwnProperty('order') && this.settings.sortBy.order.toString() === 'desc') {
+ nearestLoc = locationset[locationset.length - 1];
+ } else {
+ nearestLoc = locationset[0];
+ }
+ }
}
- // Add markers and infowindows loop
- for (var y = 0; y <= storeNumToShow - 1; y++) {
- var letter = '';
+ // Custom sorting
+ _this.sortCustom(locationset);
+ } else {
+ // Sort the multi-dimensional array by distance
+ if (typeof origin !== 'undefined') {
+ _this.sortNumerically(locationset);
+ }
- if (page > 0) {
- letter = String.fromCharCode('A'.charCodeAt(0) + (storeStart + y));
+ // 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 {
- letter = String.fromCharCode('A'.charCodeAt(0) + y);
+ if (typeof locationset[0] !== 'undefined') {
+ if (
+ _this.settings.distanceAlert !== -1 &&
+ locationset[0].distance > _this.settings.distanceAlert &&
+ (typeof paginationPage === 'undefined' || parseInt(paginationPage) === 1)
+ ) {
+ _this.notify(_this.settings.distanceErrorAlert + _this.settings.distanceAlert + ' ' + distUnit);
+ distError = true;
+ }
+ }
+ else {
+ _this.emptyResult();
+ throw new Error('No locations found. Please check the dataLocation setting and path.');
+ return;
+ }
}
-
- var point = new google.maps.LatLng(locationset[y].lat, locationset[y].lng);
- marker = _this.createMarker(point, locationset[y].name, locationset[y].address, letter, 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')) {
- bounds.extend(point);
+ }
+
+ // Save the closest location to a variable for openNearest setting
+ if (typeof locationset[0] !== 'undefined') {
+ nearestLoc = locationset[0];
+ }
+ }
+
+ // Featured locations filtering
+ if (_this.settings.featuredLocations === true) {
+
+ // Create array for featured locations
+ featuredset = $.grep(locationset, function (val) {
+ if (val.hasOwnProperty('featured')) {
+ return val.featured === 'true';
+ }
+ });
+
+ // Featured location restrictions.
+ featuredset = _this.featuredRestrictions(mappingObject);
+
+ // Create array for normal locations
+ normalset = $.grep(locationset, function (val) {
+ if (val.hasOwnProperty('featured')) {
+ return val.featured !== 'true';
}
- // Pass variables to the pop-up infowindows
- _this.createInfowindow(marker, null, infowindow, storeStart, page);
+ });
+
+ // Combine the arrays
+ locationset = [];
+ locationset = featuredset.concat(normalset);
+ }
+
+ // Disable filter inputs if there are no locations with the values left.
+ if (
+ (firstRun !== true && _this.settings.exclusiveFiltering === false) ||
+ (_this.settings.fullMapStart === true && _this.settings.exclusiveFiltering === false) ||
+ (_this.settings.defaultLoc === true && _this.settings.exclusiveFiltering === false)
+ ) {
+ _this.maybeDisableFilterOptions();
+ }
+
+ // Slide in the map container
+ if (_this.settings.slideMap === true) {
+ $this.slideDown();
+ }
+
+ // Output page numbers if pagination setting is true
+ if (_this.settings.pagination === true) {
+ _this.paginationSetup(page);
+ }
+
+ // Alternative method to display no results if locations are too far away instead of all locations.
+ if (_this.settings.altDistanceNoResult === true && nearestLoc.distance > _this.settings.distanceAlert) {
+ _this.emptyResult();
+ return;
+ }
+
+ // Handle no results
+ if (_this.isEmptyObject(locationset) || locationset[0].result === 'none') {
+ _this.emptyResult();
+ return;
+ }
+
+ // 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 fullMapStart is enabled and taxFilters is reset and name search and origin are empty, swap back to the original length.
+ if (
+ _this.settings.fullMapStart === true &&
+ _this.isEmptyObject(taxFilters) &&
+ (searchInput === '' || typeof searchInput === 'undefined') &&
+ (addressInput === '' || typeof addressInput === 'undefined')
+ ) {
+ storeNum = locationset.length;
+ }
+
+ // 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);
}
- // Center and zoom if no origin or zoom was provided
- if ((_this.settings.fullMapStart === true && firstRun === true) || (_this.settings.mapSettings.zoom === 0) || (typeof origin === 'undefined')) {
- map.fitBounds(bounds);
+ 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.querystringParams !== true) ||
+ (_this.settings.mapSettings.zoom === 0) ||
+ (typeof origin === 'undefined') ||
+ (distError === true) ||
+ ((_this.settings.maxDistance === true && firstRun === false) && this.countFilters() > 0)
+ ) {
+ 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
+ window.addEventListener('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(_this.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 infowindow
+ 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 a', function (e) {
+ e.preventDefault();
+ // Run paginationChange
+ _this.paginationChange($(this).parent().attr('data-page'));
+ });
+
+ // Inline directions
+ _this.inlineDirections(_this.map, origin);
- // Create the links that focus on the related marker
- var locList = $('.' + _this.settings.locationList + ' ul');
- locList.empty();
- // Check the locationset and continue with the list setup or show no results message
- if(locationset[0].lat === 0 && locationset[0].lng === 0) {
- locList.append(noResults);
+ // 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 {
- $(markers).each(function (x) {
- var currentMarker = markers[x];
- _this.listSetup(currentMarker, storeStart, page);
- });
+ letter = String.fromCharCode('A'.charCodeAt(0) + y);
}
- // Handle clicks from the list
- $(document).on('click.'+pluginName, '.' + _this.settings.locationList + ' li', function () {
- var markerId = $(this).data('markerid');
- var selectedMarker = markers[markerId];
+ 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);
- // List click callback
- if (_this.settings.callbackListClick) {
- _this.settings.callbackListClick.call(this, markerId, selectedMarker);
- }
+ if (_this.useLegacyMarkers()) {
+ marker.set('id', y);
+ } else {
+ marker.bhslID = y;
+ }
- // Focus on the list
- $('.' + _this.settings.locationList + ' li').removeClass('list-focus');
- $('.' + _this.settings.locationList + ' li[data-markerid=' + markerId + ']').addClass('list-focus');
+ markers[y] = marker;
- 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
- );
+ // Add marker ID to location data
+ if (_this.useLegacyMarkers()) {
+ locationset[y].markerid = marker.get('id');
+ } else {
+ locationset[y].markerid = marker.bhslID;
+ }
+
+ if (this.settings.dataRaw !== null) {
+ for (var l = 0; l < this.settings.dataRaw.length; l++) {
+ if (this.settings.dataRaw[l] && this.settings.dataRaw[l].hasOwnProperty('id') && this.settings.dataRaw[l].id === locationset[y].id) {
+ this.settings.dataRaw[l].markerid = locationset[y].markerid;
+ }
}
- else {
- _this.createInfowindow(selectedMarker, listLoc, infowindow, storeStart, page);
+ }
+
+ if (
+ (_this.settings.fullMapStart === true && firstRun === true && _this.settings.querystringParams !== true) ||
+ (_this.settings.mapSettings.zoom === 0) ||
+ (typeof origin === 'undefined') ||
+ (distError === true) ||
+ ((_this.settings.maxDistance === true && firstRun === false) && this.countFilters() > 0)
+ ) {
+ 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.querystringParams !== true) ||
+ (_this.settings.mapSettings.zoom === 0) ||
+ (typeof origin === 'undefined') ||
+ (distError === true) ||
+ ((_this.settings.maxDistance === true && firstRun === false) && this.countFilters() > 0)
+ ) {
+ _this.map.fitBounds(bounds);
+
+ // Prevent zooming in too far after fitBounds
+ var zoomListener = google.maps.event.addListener(_this.map, 'idle', function() {
+ if (_this.map.getZoom() > 16) {
+ _this.map.setZoom(16);
}
+ google.maps.event.removeListener(zoomListener);
+ });
+ }
+
+ // 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 &&
+ markers.length > _this.settings.fullMapStartListLimit
+ ) {
+ 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);
});
-
- // Prevent bubbling from list content links
- $(document).on('click.'+pluginName, '.' + _this.settings.locationList + ' li a', function (e) {
+ }
+
+ // Length unit swap setup
+ if ($lengthSwap.length) {
+ _this.lengthUnitSwap($lengthSwap);
+
+ $lengthSwap.on('change.'+pluginName, function (e) {
e.stopPropagation();
+ _this.lengthUnitSwap($lengthSwap);
});
+ }
+
+ // Open nearest location.
+ _this.openNearestLocation(nearestLoc, 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);
-
- // Modal ready callback
- if (_this.settings.modal === true && _this.settings.callbackModalReady) {
- _this.settings.callbackModalReady.call(this);
+ // MarkerClusterer setup.
+ if (_this.useLegacyMarkers()) {
+ if ( typeof MarkerClusterer !== 'undefined' && _this.settings.markerCluster !== null ) {
+ var markerCluster = new MarkerClusterer(_this.map, markers, _this.settings.markerCluster);
}
-
- });
+ } else {
+ if ( typeof markerClusterer !== 'undefined' ) {
+ var customClustererParams = _this.settings.markerCluster;
+
+ new markerClusterer.MarkerClusterer({
+ markers,
+ map: _this.map,
+ customClustererParams
+ });
+ }
+ }
+
+ // 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);
+
+ // Fill in form values from query string parameters.
+ if (_this.settings.querystringParams === true) {
+ var $addressInput = $('#' + _this.settings.addressID);
+ var $searchInput = $('#' + _this.settings.searchID);
+
+ // Address field.
+ if (typeof mappingObj !== 'undefined' && mappingObj.hasOwnProperty('origin') && $addressInput.val() === '') {
+ $addressInput.val(mappingObj.origin);
+ }
+
+ // Name search field.
+ if (typeof mappingObj !== 'undefined' && mappingObj.hasOwnProperty('name') && $searchInput.val() === '') {
+ $searchInput.val(mappingObj.name);
+ }
+ }
+
+ // Modal ready callback
+ if (_this.settings.modal === true && _this.settings.callbackModalReady) {
+ _this.settings.callbackModalReady.call(this, mappingObject);
+ }
+
+ // Filters callback
+ if (_this.settings.callbackFilters) {
+ _this.settings.callbackFilters.call(this, filters, mappingObject);
+ }
+ },
+
+ /**
+ * 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);
+ }
}
});
@@ -1906,4 +4044,4 @@
};
-})(jQuery, window, document);
\ No newline at end of file
+})(jQuery, window, document);
diff --git a/src/templates/kml/kml-infowindow-description.html b/src/templates/kml/kml-infowindow-description.html
new file mode 100644
index 0000000..afababf
--- /dev/null
+++ b/src/templates/kml/kml-infowindow-description.html
@@ -0,0 +1,6 @@
+{{#location}}
+
{{name}}
+{{#if description}}
+
{{{description}}}
+{{/if}}
+{{/location}}
diff --git a/src/templates/kml/kml-location-list-description.html b/src/templates/kml/kml-location-list-description.html
new file mode 100644
index 0000000..aeaa7ec
--- /dev/null
+++ b/src/templates/kml/kml-location-list-description.html
@@ -0,0 +1,13 @@
+{{#location}}
+
+ {{marker}}
+
+
+
{{name}}
+ {{#if description}}
+
{{{description}}}
+ {{/if}}
+
+
+
+{{/location}}
diff --git a/src/templates/standard/infowindow-description.html b/src/templates/standard/infowindow-description.html
new file mode 100644
index 0000000..5981a26
--- /dev/null
+++ b/src/templates/standard/infowindow-description.html
@@ -0,0 +1,21 @@
+{{#location}}
+
{{name}}
+
{{address}}
+{{#if address2}}
+
{{address2}}
+{{/if}}
+
{{city}}{{#if city}},{{/if}} {{state}} {{postal}}
+{{#if hours1}}
+
{{hours1}}
+{{/if}}
+{{#if hours2}}
+
{{hours2}}
+{{/if}}
+{{#if hours3}}
+
{{hours3}}
+{{/if}}
+{{#if phone}}
+
{{phone}}
+{{/if}}
+
+{{/location}}
diff --git a/src/templates/standard/location-list-description.html b/src/templates/standard/location-list-description.html
new file mode 100644
index 0000000..ab1f455
--- /dev/null
+++ b/src/templates/standard/location-list-description.html
@@ -0,0 +1,26 @@
+{{#location}}
+
+ {{marker}}
+
+
+
{{name}}
+
{{address}}
+ {{#if address2}}
+
{{address2}}
+ {{/if}}
+
{{city}}{{#if city}},{{/if}} {{state}} {{postal}}
+ {{#if phone}}
+
{{phone}}
+ {{/if}}
+ {{#if web}}
+
+ {{/if}}
+ {{#if distance}}
+
{{distance}} {{length}}
+ {{#if altdistance}}
{{altdistance}} {{altlength}}
{{/if}}
+
+ {{/if}}
+
+
+
+{{/location}}
diff --git a/storelocator.jquery.json b/storelocator.jquery.json
index 1d86b4c..980b344 100644
--- a/storelocator.jquery.json
+++ b/storelocator.jquery.json
@@ -2,11 +2,11 @@
"name": "storelocator",
"title": "jQuery Google Maps Store Locator",
"description": "This jQuery plugin takes advantage of Google Maps API version 3 to create an easy to implement store locator. No back-end programming is required, you just need to feed it KML, XML, or JSON data with all the location information.",
- "keywords": ["jquery","locator","store", "location", "locations", "maps", "map", "stores", "find"],
- "version": "2.0.5",
+ "keywords": ["jquery","locator","store","dealer","location", "locations", "maps", "map", "stores", "find"],
+ "version": "3.4.1",
"author": {
"name": "Bjorn Holine",
- "url": "http://www.bjornblog.com/"
+ "url": "https://www.bjornblog.com/"
},
"repository": {
"type": "git",
@@ -25,4 +25,4 @@
"jquery": ">=1.7",
"handlebars": ">=1.0"
}
-}
\ No newline at end of file
+}
diff --git a/test/storeLocator.html b/test/storeLocator.html
index 9a75634..c6d9f83 100644
--- a/test/storeLocator.html
+++ b/test/storeLocator.html
@@ -1,16 +1,16 @@
-
+
jQuery Google Maps Store Locator Plugin Test Suite
-
+
-
+