(function ($) {
"use strict";
/**
*
* @constructor
*/
var WebManagerMap = function () {
this.confData = {};
this.selfCount = {};
this.mapInstance = {};
this.mapMarkers = {};
this.activeMap = null;
if ($(".js-mapComponent").length) {
this.initialize();
}
};
/**
*
* @param postcode
* @returns {string}
*/
WebManagerMap.prototype.getAddressForPostcode = function (componentName, postcode, houseNum) {
var address = "",
locations = this.confData[componentName].locations;
for (var i = 0; i < locations.length; i++) {
if (locations[i].address.indexOf(postcode) !== -1) {
if (houseNum !== null) {
if (locations[i].address.indexOf(houseNum) !== -1) {
address = locations[i].address;
break;
}
} else {
address = locations[i].address;
break;
}
}
}
return address;
};
/**
* Set up map instance for the map elements on the page
*/
WebManagerMap.prototype.initialize = function () {
var self = this;
// Get all the map elements with the same class.
var mapElements = $('.js-mapComponent');
var mapsUrl = '//smbmaps.ibsrv.net/world_tiles/{z}/{x}/{y}.png';
window.mapPopupTriggered = false;
// For each container that will hold a map.
mapElements.each(
function () {
// Get the component alias
var componentName = $(this).closest('[data-component-name]').data('componentName');
// Initialize a map
self.mapInstance[componentName] = L.map(this);
L.tileLayer(
mapsUrl,
{
attribution: 'Map data provided by Internet Brands',
maxZoom: 18
}
).addTo(self.mapInstance[componentName]);
self.mapInstance[componentName].scrollWheelZoom.disable();
self.confData[componentName] = JSON.parse($(this).closest('[data-component-name]').find('input[type=hidden]').val());
self.setupMap(self.mapInstance[componentName], self.confData[componentName]);
self.mapSize(this, self.confData[componentName], componentName);
self.attachEvents(this, self.confData[componentName], componentName);
var searchSubmit = false;
self.searchByZipCode(this, self.confData[componentName], self.mapInstance[componentName], componentName, searchSubmit);
var locations = self.confData[componentName].locations;
// have to make array reverse because the centered marker should be the first one in the listing
// of locations manager
if(locations.length > 1 && !self.confData[componentName].fitBounds){
locations = locations.reverse();
}
for (var i = 0; i < locations.length; i++) {
//if there is latitude and longitude for the location
if (locations[i].latitude != null && locations[i].longitude != null) {
var json = [{
"lat": locations[i].latitude,
"lon": locations[i].longitude,
"address": {
"name": locations[i].address,
}
}];
// create the marker
self.applyData(json, componentName, this, i);
}
// if there is an address for the location
else {
//get the latitude for the current location
self.geolocateLocation(locations[i], componentName, this);
}
}
var xaxisPan = self.confData[componentName].xaxis,
yaxisPan = self.confData[componentName].yaxis;
// set map X/Y - Axis Offset for desktop resolution
if ($(window).width() > 768) {
if(xaxisPan != 0 || yaxisPan != 0) self.mapInstance[componentName].panBy([xaxisPan, yaxisPan])
}
}
);
// Toggle map scrolling
$("body").click(function (e) {
if ($(e.target).hasClass("js-mapComponent")) {
var componentName = $(e.target).closest('[data-component-name]').data('componentName');
// Disable scroll on other map instance if already enabled
if (self.activeMap !== null) {
self.mapInstance[self.activeMap].scrollWheelZoom.disable();
}
self.activeMap = componentName;
if(self.confData[componentName].zoomControl){
self.mapInstance[componentName].scrollWheelZoom.enable();
}
} else {
if (self.activeMap !== null) {
self.mapInstance[self.activeMap].scrollWheelZoom.disable();
self.activeMap = null;
}
}
});
};
/**
* Attach events
*
* @param map
* @param mapData
* @param componentName
*/
WebManagerMap.prototype.attachEvents = function (map, mapData, componentName) {
var self = this,
winResizeTimer;
$(window).resize(function () {
self.mapSize(map, mapData, componentName);
clearTimeout(winResizeTimer);
//Call mapCenter function when the resizing completely will stop/finish
winResizeTimer = setTimeout(function () {
if (self.mapMarkers[componentName].length > 1 && self.confData[componentName].fitBounds) {
var markerGroup = L.featureGroup(self.mapMarkers[componentName]);
self.mapCenter(componentName, markerGroup);
}
}, 250);
});
}
/**
*
* @param mapInstance
* @param mapData
*/
WebManagerMap.prototype.setupMap = function (mapInstance, mapData) {
var zoomControl = mapData.zoomControl;
if (!zoomControl) {
// Drawing a square area to zoom
mapInstance.boxZoom.disable();
// The zoom on double click
mapInstance.doubleClickZoom.disable();
// Like when you hit the keys for + and -
mapInstance.keyboard.disable();
// The zoom with scroll
mapInstance.scrollWheelZoom.disable();
if (mapInstance.tap) {
mapInstance.tap.disable();
}
// The zoom via touch
mapInstance.touchZoom.disable();
// The + and - buttons
$(".leaflet-control-zoom").css("visibility", "hidden");
}
};
/**
* Set fit bound for map instance with multiple locations
* @param componentName Alias of the map component
* @param markerLayer The layer containing all the markers
*/
WebManagerMap.prototype.mapCenter = function (componentName, markerLayer) {
var self = this;
self.mapInstance[componentName].fitBounds(markerLayer.getBounds().pad(0.5));
if (self.confData[componentName].alwaysShowInfoWindow) {
if(self.mapMarkers[componentName][self.mapMarkers[componentName].length - 1].isPopupOpen()){
if (!window.mapPopupTriggered) {
//TODO - mapCenter function triggers endless resize event
//SMBWMGR-19261 - this does the trick, togglePopup does not help since need some timeout to correct the position
self.mapMarkers[componentName][self.mapMarkers[componentName].length - 1].closePopup();
self.mapMarkers[componentName][self.mapMarkers[componentName].length - 1].openPopup();
window.mapPopupTriggered = true;
}
}
}
};
/**
* Calculates the size of the map based on the settings
* @param element Map instance
* @param mapData Settings for the map
*/
WebManagerMap.prototype.mapSize = function (element, mapData, componentName) {
var self = this, parent = $(element).parent();
if (mapData.autoFit) {
var titleCaptionHeight, pleMapAwrapHeight;
(!self.confData[componentName].isPle) ? titleCaptionHeight = parent.children('.component__title-caption-wrap').outerHeight(true) : pleMapAwrapHeight = parent.find('.map__wrap').outerHeight(true);
parent.css('height', '100%');
var elementHeight = parent.height();
// If there title or caption is shown in the component subtract it from the parent height
if (!self.confData[componentName].isPle && titleCaptionHeight !== null) {
elementHeight = elementHeight - titleCaptionHeight;
} else {
if ($(window).width() <= 768 && pleMapAwrapHeight !== null) {
elementHeight = elementHeight - pleMapAwrapHeight;
}
}
// Resize the map and trigger map.resize event
$(element).css({width: parent.width(), height: elementHeight});
$(element).trigger('map.resize');
} else {
$(element).css({width: mapData.width, height: mapData.height});
}
};
/**
* Notice the param json_callback=jsonMapCallback
* Check more here: http://wiki.openstreetmap.org/wiki/Nominatim
* json_callback= - Wrap json output in a callback function (JSONP) i.e. ()
* @type {string}
*/
WebManagerMap.prototype.geocodeURL = "https://nominatim.openstreetmap.org/search?[QUERY]&format=json&polygon=1&addressdetails=1&limit=1";
/**
*
* @param location
*/
WebManagerMap.prototype.geolocateLocation = function (location, componentName, mapElement) {
var self = this,
geocodeURL = this.geocodeURL;
geocodeURL = geocodeURL.replace("[QUERY]", location.query);
// Geocode the location and apply the response to the map instance
$.ajax({
url: geocodeURL,
dataType: "jsonp",
jsonp: 'json_callback',
success: function (json) {
self.applyData(json, componentName, mapElement);
}
});
// @todo: Maybe find another solution for this cross-domain query
};
/**
* Geocode the location, search by zip code or city
* @param map
* @param mapData
* @param mapInstance
* @param componentName
*/
WebManagerMap.prototype.searchByZipCode = function (map, mapData, mapInstance, componentName, searchSubmit) {
var self = this,
geocodeURL = this.geocodeURL;
$("#location-zip").submit(function (event) {
event.preventDefault();
var zipCodeVal = $('#location-search').val();
if (!zipCodeVal || zipCodeVal == "Enter your zip code and state") {
alert("Please enter your current city or ZIP code.");
return;
}
geocodeURL = "https://nominatim.openstreetmap.org/search?q=" + zipCodeVal + "&format=json";
$.ajax({
url: geocodeURL,
dataType: "jsonp",
jsonp: 'json_callback',
success: function (json) {
self.getDistance(json, mapData, mapInstance, componentName, searchSubmit);
}
});
});
};
/**
* Calculate the distance between the locations
* @param json
* @param mapData
* @param mapInstance
* @param componentName
*/
WebManagerMap.prototype.getDistance = function (json, mapData, mapInstance, componentName, searchSubmit) {
var self = this;
self.markerTemp;
if (Object.keys(json).length !== 0) {
// get the coordinates
var location = mapData.locations,
lat = json[0].lat,
lon = json[0].lon;
// create green icon for the new marker
var greenIcon = new L.Icon({
iconUrl: mapData.assetsPath + 'css/images/marker-icon-green.png',
shadowUrl: mapData.assetsPath + 'images/marker-shadow.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
shadowSize: [41, 41]
});
// create and attach the new marker to the map instace
var marker = new L.marker([lat, lon], { icon: greenIcon });
marker.addTo(mapInstance);
// set the view to the new marker
// mapInstance.setView([lat, lon], mapData.zoom, {animation: true});
// remove the previous markers generated by the search
if (typeof (self.markerTemp) != "undefined") {
self.markerTemp.removeFrom(mapInstance);
this.mapMarkers[componentName].pop(marker);
}
// keep in temp previous marker and push the new one
if (marker != null) self.markerTemp = marker;
this.mapMarkers[componentName].push(marker);
// close all of the popups
mapInstance.closePopup();
// set the view based on the grouped markers
var markerGroup = L.featureGroup(this.mapMarkers[componentName]).addTo(mapInstance);
mapInstance.fitBounds(markerGroup.getBounds().pad(0.5));
// loop and set the miles values
for (var i = 0; i < location.length; i++) {
if (location[i].latitude != null && location[i].longitude != null) {
var distanceMeters = L.latLng([location[i].latitude, location[i].longitude]).distanceTo([lat, lon]),
// distanceTo returns meters, convert this to miles - 1m = 0.000621371
distanceMiles = Math.round(distanceMeters * 0.000621371),
$gmapLocTitle = $(".gmap-location b"),
$gmapLocDistance = $(".gmap-location .gmap-distance");
for (var j = 0; j < $gmapLocTitle.length; j++) {
if ($gmapLocTitle[j].innerText.indexOf(location[i].title) !== -1) {
$($gmapLocTitle[j]).closest('.gmap-location').data('distanceMiles', distanceMiles);
distanceMiles = distanceMiles.toLocaleString();
$gmapLocDistance.show();
$gmapLocDistance[j].innerHTML = distanceMiles + " mi";
}
}
}
}
// order the location list by distance
$(".gmap-location-list li").sort(function (a, b) { return ($(b).data('distanceMiles')) < ($(a).data('distanceMiles')) ? 1 : -1; }).appendTo('.gmap-location-list');
// trigger the click and change the location info
searchSubmit = true;
$(".gmap-location-list li").first().children().trigger('click',[ searchSubmit ]);
} else {
// clean up the distance data
var $gmapLocTitle = $(".gmap-location b"),
$locDistanceInfo = $(".map-search__location-distance > b"),
$gmapLocDistance = $(".gmap-location .gmap-distance");
for (var i = 0; i < $gmapLocDistance.length; i++) {
$gmapLocDistance.show();
$gmapLocDistance[i].innerHTML = " -- mi";
}
for (var i = 0; i < $locDistanceInfo.length; i++) {
$locDistanceInfo.show();
$locDistanceInfo[i].innerHTML = " -- mi";
}
for (var i = 0; i < $gmapLocTitle.length; i++) {
$($gmapLocTitle[i]).closest('.gmap-location').data('distanceMiles', "--");
}
}
};
/**
* Apply the location data on the map instance
* @param json
* @param componentName
*/
WebManagerMap.prototype.applyData = function (json, componentName, mapElement, index) {
var self = this;
if (json.length > 0) {
self.selfCount[componentName] = self.selfCount[componentName] || 0;
var data = json[0],
lat = data.lat,
lon = data.lon;
self.mapInstance[componentName].setView([lat, lon], self.confData[componentName].zoom, {animation: true});
self.selfCount[componentName]++;
var markerOptions = [];
markerOptions['alt'] = 'Map Marker';
var assetsPath = self.confData[componentName].assetsPath;
if (self.confData[componentName].iconUrl) {
var iconUrl = self.confData[componentName].iconUrl,
icon = new L.Icon({
iconUrl: iconUrl,
shadowUrl: assetsPath + 'images/marker-shadow.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
shadowSize: [41, 41]
});
markerOptions['icon'] = icon;
}
// var marker = L.marker([lat, lon], markerOptions).addTo(self.mapInstance[componentName]);
var marker = L.marker([lat, lon], markerOptions);
if (self.confData[componentName].alwaysShowInfoWindow || self.confData[componentName].showAddressOnHover) {
var location = data.display_name || null; // Default to display_name attribute or null
var addressFromPostcode = null;
if ('postcode' in data.address) {
var houseNum = ('house_number' in data.address) ? data.address.house_number : null;
addressFromPostcode = self.getAddressForPostcode(componentName, data.address.postcode, houseNum);
location = (addressFromPostcode != '') ? addressFromPostcode : location;
}
else if (data.address.name) {
location = data.address.name;
}
if (location) {
marker.bindPopup(location);
}
}
self.mapMarkers[componentName] = self.mapMarkers[componentName] || [];
self.mapMarkers[componentName].push(marker);
// TODO: this needs to be in the map init section?
if (self.confData[componentName].hue) {
var hueObj = self.confData[componentName].hue,
hueStylers = hueObj.stylers,
// @todo: Maybe find a way to convert gama, lightness and the hue (rbg color by some reason) into a rgb?
gama = hueStylers[0].gama,
lightness = hueStylers[1].lightness,
hue = hueStylers[2].hue;
if (hue !== "none") {
$(mapElement).css("background-color", hue);
$("div.leaflet-tile-pane", mapElement).css("opacity", "0.9");
}
}
// Add the markers to the map
if (self.selfCount[componentName] >= self.confData[componentName].locations.length) {
var markerGroup = L.featureGroup(self.mapMarkers[componentName]).addTo(self.mapInstance[componentName]);
// Center the map if more then one marker
if (self.mapMarkers[componentName].length > 1 && self.confData[componentName].fitBounds) {
self.mapCenter(componentName, markerGroup);
}
// Open the info window if the setting is set
if (self.confData[componentName].alwaysShowInfoWindow) {
//SMBWMGR-19261 - this does the trick, togglePopup does not help since need some timeout to correct the position
self.mapMarkers[componentName][self.mapMarkers[componentName].length - 1].closePopup();
self.mapMarkers[componentName][self.mapMarkers[componentName].length - 1].openPopup();
}
}
} else {
console.log("No data found.");
}
};
/**
* PLE specific to change text based on clicked location
*/
function pleMap(){
var locationFields = [
{
selector: '.map-search__location-distance h3 b',
property: 'title',
type: 'text'
},
{
selector: '.map-search__location-address p',
property: 'address_format',
type: 'text'
},
{
selector: '.map-search__location-phone',
property: 'phone',
type: 'text'
},
{
selector: '.map-search__location-phone2',
property: 'phone2',
type: 'text'
},
{
selector: '.map-search__location-fax',
property: 'fax',
type: 'text'
},
{
selector: '.map-search__location-directions a',
property: ['cta','buttonText'],
type: 'text'
},
{
selector: '.map-search__location-directions a',
property: ['cta','linkPage'],
type: 'href'
},
{
selector: '.map-search__email',
property: 'email',
type: 'text'
}
];
$('#gmap-location-list').on('click', '.gmap-location a', function (event, searchSubmit) {
event.preventDefault();
var componentName = $(this).closest('[data-component-name]').data('componentName');
var locations = window.webManagerMap.confData[componentName].locations;
var clickedID = $(this).parent().attr('id');
var location = locations.find(function(obj) {
return obj.ID === clickedID
});
var locMarker = window.webManagerMap.mapMarkers[componentName].find(function(obj) {
return (obj._latlng.lat == location.latitude && obj._latlng.lng == location.longitude);
});
// set the view to the clicked marker
var mapInstance = window.webManagerMap.mapInstance[componentName],
mapData = window.webManagerMap.confData[componentName];
if (typeof(searchSubmit) == "undefined") {
mapInstance.setView([location.latitude, location.longitude], mapData.zoom, {animation: true});
if(typeof(locMarker) != "undefined") locMarker.openPopup();
}
// set the distance
var distance = $(this).closest('.gmap-location').data('distanceMiles'),
$locDistanceInfo = $('.map-search__location-distance > b');
if (typeof(distance) != "undefined") {
$locDistanceInfo.show();
distance = distance.toLocaleString();
$locDistanceInfo[0].innerHTML = distance + " mi";
} else {
$locDistanceInfo[0].innerHTML = " -- mi";
}
locationFields.forEach(function (field, i) {
var loc = {};
var prop = field.property;
if(typeof prop === 'string' || prop instanceof String){
loc = location[prop]
}else{
var lc = location;
prop.forEach(function (pr) {
lc = lc[pr];
});
loc = lc;
}
if( typeof loc === 'string' || loc instanceof String){
if(field.type === 'text'){
$(field.selector).text(loc);
if(field.selector == '.map-search__location-phone' || field.selector == '.map-search__location-phone2'){
$(field.selector).attr('href','tel:'+loc);
}
}else if(field.type === 'href'){
$(field.selector).attr('href', loc);
}
if(loc.length > 0 ){
$(field.selector).show();
}else{
$(field.selector).hide();
}
}else{
var vals = Object.values(Object.keys(location[prop]).map(function(e) {
return location[prop][e];
}));
$(field.selector).html(vals.join("
"));
}
});
});
}
$(document).ready(
function () {
window.webManagerMap = new WebManagerMap();
if($('.map-a').length > 0){
pleMap();
}
}
);
})(window.jQuery);