Files
2026-04-13 15:50:16 +02:00

803 lines
22 KiB
JavaScript

;(function ($, hotspots, undefined) {
"use strict";
var Leaflet = L.noConflict();
var ua = window.navigator.userAgent,
isiOS = !!ua.match(/iPad/i) || !!ua.match(/iPhone/i),
isWebkit = !!ua.match(/WebKit/i),
isWebkitiOS = isiOS && isWebkit,
isMobileSafari = isiOS && isWebkit && !ua.match(/CriOS/i),
getBrowserVersion = function(){
var temp,
M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, '-?'];
if ((temp = ua.match(/version\/(\d+)/i)) != null) {
M.splice(1, 1, temp[1]);
}
return parseInt(M[1]);
}
// Store all the leaflets on the page in an array for access later
var leaflets = [];
// Store all image maps on the page in an object for access later
var imageMaps = {};
// Store all the more info area hotspots in an object for access later
hotspots.infoSpots = {};
var mapSetup = function(){
$('.da-error').hide();
$('.hotspot-info').addClass('da-hidden');
var images = $('img.hotspots-image, picture.hotspots-image img'); // Select images as well as images inside picture elements
images.each(function(){
var img = $(this);
var tester = new Image();
tester.onload = function(){
markLoaded(img);
moreInfoSetup(img);
leafletSetup(img);
};
tester.src = img.attr('src');
});
if (isMobileSafari) {
window.onpageshow = function(event){
if (event.persisted) {
window.location.reload();
}
};
}
};
var markLoaded = function(img) {
img.data('status', 'loaded');
var container = img.parents('.hotspots-container').addClass('loaded');
img.attr('aria-hidden', true);
};
var stopAudioVideo = function(el) {
var iframe = el.querySelector( 'iframe[src*=youtube]');
var video = el.querySelector( 'video' );
var audio = el.querySelector( 'audio' );
if (iframe) {
var iframeSrc = iframe.src;
iframe.src = iframeSrc;
}
if (video) {
video.pause();
}
if (audio) {
audio.pause();
}
}
var moreInfoSetup = function(img) {
var container = img.parents('.hotspots-container');
if (container.data('layout') == 'tooltip') {
return;
}
if (container.data('layout') == 'lightbox') {
lightboxSetup(container, img);
return;
}
infoboxSetup(container, img);
};
var infoboxSetup = function(container, img) {
var initial = container.find('.hotspot-initial');
var content = container.find('.hotspots-placeholder');
initial.addClass('visible');
container.on('active.responsilight inactive.responsilight', function(e){
var data = $(e.target).data('areaData');
var info;
if (e.type === 'active') {
info = $(data.href);
} else {
info = initial;
}
var visibleContent = content.children('.visible');
stopAudioVideo(visibleContent.get(0));
visibleContent.removeClass('visible');
info.removeClass('da-hidden').addClass('visible').appendTo(content);
// Check for an embedded video player
var video = info.find('.wp-video');
if (video.length) {
if (!info.data('video-resized')) {
info.data('video-resized', true);
window.dispatchEvent(new Event('resize'));
}
}
});
};
var lightboxSetup = function(container, img) {
container.on('active.responsilight', function(e){
var data = $(e.target).data('areaData'),
info = $(data.href),
container = info.parent(),
target = $(e.target),
currentLightbox = $('.featherlight');
if (e.type === 'active' && currentLightbox.length === 0) {
$.featherlight('<div class="hotspot-info"></div>', {
closeLabel: drawattentionData.closeLabel,
closeSpeed: 250,
closeOnEsc: false,
afterContent: function(){
var content = $('.featherlight-inner'),
lb = $('.featherlight-content'),
mapNo = img.data('id');
info.appendTo(content).show();
lb.addClass('lightbox-' + mapNo);
// Fix accessibility issue - links not focusable by keyboard
var untabbables = info.find('a[tabindex=-1]');
untabbables.attr('tabindex', 0);
setTimeout(function(){
var img = content.find('img'),
contentHeight = content.get(0).scrollHeight,
imgHeight = img.height(),
lbHeight = lb.outerHeight();
if (contentHeight > lbHeight) {
var diff = contentHeight - lbHeight + 50,
newHeight = imgHeight - diff,
minHeight = $(window).innerHeight()/2;
newHeight = newHeight < minHeight ? minHeight : newHeight;
var naturalHeight = img.prop('naturalHeight');
if (newHeight < naturalHeight) {
img.css({'width': 'auto'});
img.animate({
'height': newHeight
}, 200);
}
}
}, 100);
lightboxAnchorLinks(content, target);
window.dispatchEvent(new Event('resize')); // Trigger window resize event for [video] shortcode JS
},
afterOpen: function() {
$('body').on('keyup', documentEsc);
},
afterClose: function(){
target.removeClass('hotspot-active');
$('body').off('keyup', documentEsc);
},
beforeClose: function(){
stopAudioVideo(document.querySelector('.featherlight-content'));
setTimeout(function(){
info.hide().appendTo(container);
}, 500); // delay hiding content and moving it back outside the lightbox
}
});
}
});
};
var documentEsc = function(e) {
if (e.keyCode === 27) {
$.featherlight.current().close();
}
};
var lightboxAnchorLinks = function(content, hotspot){
var links = content.find('.hotspot-content a[href^="#"]');
links.on('click', function(e){
e.preventDefault();
var targetEl = $(e.target.hash);
var current = $.featherlight.current();
if (!current) return
current.afterClose = function(){
hotspot.removeClass('hotspot-active');
$('html').removeClass('with-featherlight');
$('html, body').animate({
scrollTop: targetEl.offset().top
}, 500);
}
current.close();
});
};
var showTooltip = function(shape, areaData) {
var container = $(shape._map._container);
var content = $(areaData.href).html();
var tip = Leaflet.responsivePopup({
autoPan: false,
closeButton: areaData.trigger == 'click',
hasTip: container.width() > 840,
maxHeight: container.height() * .9,
offset: new Leaflet.Point(0,0)
});
tip.setContent(content);
shape.bindPopup(tip);
if (areaData.trigger === 'click') {
shape.on('click', function(e){
if (shape._path.classList.contains('hotspot-active')) {
shape.closePopup()
} else {
shape.openPopup();
}
});
} else {
shape.on('mouseover', function(){
shape.openPopup();
});
shape.on('mouseout', function(){
shape.closePopup();
});
}
container.on('click', function(e){
e.stopPropagation();
});
$(document).on('click', function(e){
shape.closePopup();
shape._path.classList.remove('hotspot-active');
});
};
var leafletSetup = function(img) {
var id = img.data('id');
var container = $('<div id="hotspots-map-container-' + id + '" class="hotspots-map-container"></div>');
var imgWidth = img.width();
var imgHeight = img.height();
container.css({
'width': imgWidth + 'px',
'height': imgHeight + 'px'
});
img.after(container);
// Check to be sure the container is on the page
// To prevent errors when loaded in Elementor popup
var containerTest = $('#hotspots-map-container-' + id);
if (!containerTest.length) {
return
}
var map = Leaflet.map('hotspots-map-container-' + id, {
attributionControl: false,
boxZoom: false,
crs: Leaflet.CRS.Simple,
doubleClickZoom: false,
dragging: false,
keyboard: false,
minZoom: -20,
scrollWheelZoom: false,
// tap: !isWebkitiOS,
tap: true,
touchZoom: false,
zoomControl: false,
zoomSnap: 0,
});
var domImg = img.get(0);
var natHeight = domImg.naturalHeight;
var natWidth = domImg.naturalWidth;
img.data('natW', natWidth);
img.data('natH', natHeight);
var bounds = [[0,0], [natHeight, natWidth]];
var imageLayer = Leaflet.imageOverlay(img.attr('src'), bounds).addTo(map);
map.fitBounds(bounds);
leaflets.push({
map: map,
img: img
});
drawSpots(img, map);
};
var showContextMenu = function(shape, areaData, e) {
var hash = areaData.href;
var container = $(shape._map._container);
if (!hash) {
return;
}
$('.da-address-wrapper').remove();
var windowAddress = window.location.href.split('#')[0];
var shapeAddress = windowAddress + hash;
var div = $('<div class="da-address-wrapper"></div>')
.css({
'left': e.originalEvent.pageX + 'px',
'top': e.originalEvent.pageY + 'px'
})
.append('<p>' + shapeAddress + '</p>')
.append('<span class="da-address-close">&times;</span>')
.appendTo(document.body);
div.find('.da-address-close').on('click', function(){
div.remove();
});
};
var drawSpots = function(img, map) {
var id = img.data('id');
/* If the image has a usemap attribute, detach the map and store it in the imageMaps object */
if (img[0].hasAttribute('usemap')) {
var mapName = img.attr('usemap').replace('#', '');
var imageMap = $('map[name="' + mapName + '"]');
imageMaps[id] = imageMap.detach();
img.removeAttr('usemap');
} else { /* Else we've already removed the image map, so we just need to get it */
var imageMap = imageMaps[id];
}
var areas = imageMap.find('area');
var container = img.parents('.hotspots-container');
areas.each(function(){
var area = $(this);
var shape = area.attr('shape');
var coords = area.attr('coords').split(',');
var areaData = {
style: area.data('color-scheme') ? area.data('color-scheme') : 'default',
title: area.attr('title'),
href: area.attr('href'),
spot: area.data('id'),
target: area.attr('target'),
action: area.data('action'),
layout: container.data('layout'),
trigger: container.data('trigger')
};
switch(shape) {
case 'circle':
renderCircle(coords, map, img, areaData);
break;
case 'rect':
renderPoly(coords, map, img, areaData);
break
case 'polygon':
renderPoly(coords, map, img, areaData);
break;
case 'poly': // Leaving this here for backward compatibility
renderPoly(coords, map, img, areaData);
break;
}
});
// A11y fixes after all the spots are drawn
a11yFixes(img, map);
// Link to area after all the spots are drawn
hotspots.linkToArea();
};
var renderCircle = function(coords, map, img, areaData) {
var x = coords[0];
var y = img.data('natH') - coords[1];
var rad = coords[2];
var circle = Leaflet.circle([y,x], {
radius: rad,
className: 'hotspot-' + areaData.style,
title: areaData.title
});
circle.on('add', function(e) {
var $circle = $(e.target.getElement());
$circle.data('areaData', areaData);
$circle.attr('tabindex', '0');
$circle.attr('aria-label', areaData.title);
$circle.attr('name', areaData.title);
});
circle.addTo(map);
// If this is a more info hotspot, add it to the infoSpots object
if (areaData.href.charAt(0) === '#') {
var spotName = areaData.href.replace('#', '');
hotspots.infoSpots[spotName] = circle;
}
shapeEvents(circle, areaData);
};
var renderPoly = function (coords, map, img, areaData) {
var xCoords = [];
var yCoords = [];
for (var i = 0; i < coords.length; i++) {
if (i % 2 == 0) {
xCoords.push(coords[i]);
} else {
yCoords.push(coords[i]);
}
}
var polyCoords = yCoords.map(function(coord, index) {
return [img.data('natH') - coord, xCoords[index]];
});
var poly = Leaflet.polygon(polyCoords, {
className: 'hotspot-' + areaData.style,
title: areaData.title
});
// Attach the area data to the path as soon as it's added to the map, a11y attributes
poly.on('add', function(e) {
var $poly = $(e.target.getElement());
$poly.data('areaData', areaData);
$poly.attr('tabindex', '0');
$poly.attr('aria-label', areaData.title);
$poly.attr('name', areaData.title);
});
poly.addTo(map);
// If this is a more info hotspot, add it to the infoSpots object
if (areaData.href.charAt(0) === '#') {
var spotName = areaData.href.replace('#', '');
hotspots.infoSpots[spotName] = poly;
}
shapeEvents(poly, areaData);
};
var shapeOver = function(shape, areaData, e) {
var $shape = $(e.target.getElement());
$shape.trigger('over.responsilight');
if (areaData.trigger === 'hover' && e.type !== 'touchstart' & e.type !== 'keypress') {
$shape.addClass('hotspot-active');
$shape.trigger('active.responsilight');
}
};
var shapeOut = function(shape, areaData, e) {
var $shape = $(e.target.getElement());
$shape.trigger('out.responsilight');
if (areaData.trigger === 'hover' && e.type !== 'keypress') {
$shape.removeClass('hotspot-active');
$shape.trigger('inactive.responsilight');
// If mouse user, blur hover spots when mouse moves out
if (e.type === 'mouseout') {
$shape.trigger('blur');
}
}
};
var shapeClick = function(shape, areaData, e) {
var $shape = $(e.target.getElement());
$shape.trigger('areaClick.responsilight');
if (areaData.trigger === 'hover' && e.type !== 'touchstart' && e.type !== 'keypress' && !isMobileSafari) {
return;
}
$shape.toggleClass('hotspot-active');
if ($shape.hasClass('hotspot-active')) {
$shape.trigger('active.responsilight');
} else {
$shape.trigger('inactive.responsilight');
}
var oldActive = $shape.siblings('.hotspot-active');
if (oldActive.length) {
oldActive.removeClass('hotspot-active');
}
};
var shapeEvents = function(shape, areaData) {
// Handle URL spots
if (areaData.action == 'url') {
if (areaData.title) {
shape.bindTooltip(areaData.title);
}
shape.on('click', function(e) {
if (areaData.target == '_new' && !isMobileSafari) { // new window
window.open(areaData.href, '_blank');
} else { // same window
var first = areaData.href.charAt(0);
var targetElem;
try {
targetElem = first === '#' ? $(areaData.href) : null;
} catch (error) {
targetElem = null
}
if (targetElem && targetElem.length) { // hash link to existing target
$('html, body').animate({
scrollTop: targetElem.offset().top - 50
}, 750, function(){ // callback after scrolling
history.pushState({}, '', areaData.href);
})
} else {
window.location = areaData.href;
}
}
});
return;
}
// Show right-click context menu for logged-in admins only
if (drawattentionData.isLoggedIn && drawattentionData.isAdmin) {
shape.on('contextmenu', function(e){
showContextMenu(shape, areaData, e);
});
}
// Handle tooltip spots
if (areaData.layout === 'tooltip') {
showTooltip(shape, areaData);
}
// Add styled tooltip to all non-hover areas
if (areaData.action === 'url' || areaData.trigger === 'click' && areaData.layout !== 'tooltip') {
if (areaData.title) {
shape.bindTooltip(areaData.title);
}
}
// Handle all other spots
var moved = false;
shape.on('touchstart touchmove touchend click mouseover mouseout keypress focus blur', function(e){
switch(e.type) {
case 'touchstart':
moved = false;
break;
case 'touchmove':
moved = true;
break;
case 'touchend':
if (moved) {
return;
}
shapeOver(shape, areaData, e);
shapeClick(shape, areaData, e);
break;
case 'click':
shapeClick(shape, areaData, e);
break;
case 'keypress':
var key = e.originalEvent.which;
if ( key == 13 || key == 32) {
e.originalEvent.preventDefault();
shapeClick(shape, areaData, e);
}
break;
case 'mouseover':
if (!isMobileSafari || (isMobileSafari && getBrowserVersion < 13)) {
shapeOver(shape, areaData, e);
}
if (isMobileSafari && !(getBrowserVersion() >= 12)) {
shapeClick(shape, areaData, e);
}
break;
case 'focus':
shapeOver(shape, areaData, e);
break;
case 'blur':
shapeOut(shape, areaData, e);
break;
case 'mouseout':
shapeOut(shape, areaData, e);
break;
}
});
};
var a11yFixes = function(img, map){
let svg = img.siblings('.leaflet-container').find('.leaflet-overlay-pane svg');
let id = img.data('id');
// Add title and description to SVG
svg.prepend('<desc id="img-desc-' + id + '">' + img.data('image-description') + '</desc>');
svg.prepend('<title id="img-title-' + id + '">' + img.data('image-title') + '</title>');
// Use title and desc to describe the svg
svg.attr('aria-labelledby', 'img-title-' + id);
svg.attr('aria-describedby', 'img-desc-' + id);
// Make svg screen reader traversable
svg.attr('role', 'group');
// Create a traversable list
var group = svg.find('g').attr('role', 'list');
group.children().attr('role', 'listitem');
// Handle tooltips for the map
map.on('popupopen',function(popup) {
// shift focus to the popup when it opens
$(popup.popup._container).find('.leaflet-rrose-content').attr('tabindex','-1').focus();
// move the close button to the end of the popup content so screen readers reach it
// after the main popup content, not before
var close = $(popup.popup._container).find('.leaflet-rrose-close-button').detach();
close.attr('aria-label','Close item');
$(popup.popup._container).append(close);
$(document).on('keydown', function(e){
if (e.which == 27) {
map.closePopup();
}
});
});
// return focus to the icon we started from before opening the pop up
map.on('popupclose',function(popup) {
$(document).off('keydown');
$(popup.popup._source._path).focus();
});
};
hotspots.linkToArea = function(){ // Called after the shapes are drawn
var hash = window.location.hash;
if (!hash) return;
var spotName = hash.replace('#', '');
if (!hotspots.infoSpots.hasOwnProperty(spotName)) return;
Object.keys(hotspots.infoSpots).forEach(function(key) {
hotspots.infoSpots[key].closeTooltip()
})
hotspots.infoSpots[spotName].fire('click')
};
hotspots.setup = function(){
mapSetup();
};
hotspots.resizeTimer = null;
hotspots.resizing = false;
hotspots.init = function(){ // For backward compatibility - resets the size of the leaflet on demand
leaflets.forEach(function(item){
var isLoaded = item.img.data('status') === 'loaded';
if (!isLoaded) {
return;
}
item.img.next('.hotspots-map-container').css({
'width': item.img.width() + 'px',
'height': item.img.height() + 'px'
});
item.map.invalidateSize(true);
item.map.fitBounds([[0,0], [item.img.data('natH'), item.img.data('natW')]]);
});
};
hotspots.compatibilityFixes = function(){
if (window.Foundation) { /* Fix for Foundation firing tag change event indiscriminately when some items are clicked on the page */
$(window).on('change.zf.tabs', function(e) {
if (e.target.tagName !== 'INPUT') {
hotspots.init();
}
});
}
$(window).on('pageloaded load', function() { /* Listen for pageloaded and load events on the window */
hotspots.init();
});
$(window).on('et_hashchange', function() { /* Listen for Divi hashchange */
setTimeout(function() {
hotspots.init();
}, 1000);
});
$('.ult_tabs').on('click', '.ult_tab a', function() { /* Ultimate tabs */
setTimeout(function() {
hotspots.init();
}, 2000);
});
$('a[data-vc-accordion], .et_pb_tabs .et_pb_tabs_controls li, .et_pb_toggle_title').on('click', function() { /* Divi tabs, accordion, toggle */
setTimeout(function() {
hotspots.init();
}, 1000);
});
$('.vc_tta-tabs-container').on('click', '.vc_tta-tab', function() { /* Visual composer tabs */
setTimeout(function() {
hotspots.init();
}, 1000);
});
$('.ui-tabs-anchor, .nav-tabs > li').on('click', function() { /* UI Tabs */
setTimeout(function() {
hotspots.init();
}, 750);
});
$('.fl-accordion-button').on('click', function() { /* Beaver Builder accordion */
setTimeout(function() {
hotspots.init();
}, 500);
});
$('.responsive-tabs').on('click', '.responsive-tabs__list__item', function() { /* Responsive tabs */
hotspots.init();
});
$('.elementor-tabs').on('click', '.elementor-tab-title', function() { /* Elementor tabs */
hotspots.init();
});
$('.fl-tabs').on('click', 'a.fl-tabs-label', function() { /* Beaver Builder tabs */
hotspots.init();
});
$('.uabb-adv-accordion-button .uabb-tabs').on('click', function() { /* Ultimate Beaver Builder addons accordion and tabs */
setTimeout(function() {
hotspots.init();
}, 250);
});
$('.w-tabs-item').on('click', function() { /* Custom Woocommerce tabs */
setTimeout(function() {
hotspots.init();
}, 1000);
});
};
}(jQuery, window.hotspots = window.hotspots || {}));
jQuery(function(){
hotspots.setup();
hotspots.compatibilityFixes();
});
jQuery(document).on('elementor/popup/show', function(){
hotspots.setup();
hotspots.compatibilityFixes();
});
jQuery(window).on('resize orientationchange', function(e){
var $window = jQuery(this);
if (!hotspots.resizing) {
$window.trigger('resizeStart.responsilight');
hotspots.resizing = true;
}
clearTimeout(hotspots.resizeTimer);
hotspots.resizeTimer = setTimeout(function(){
hotspots.init();
$window.trigger('resizeComplete.responsilight');
hotspots.resizing = false;
}, 250);
});
jQuery(window).on('hashchange', function(){
hotspots.linkToArea();
});
window.onerror = function(errorMsg, url, lineNumber) { // This should be a fun little experiement!
var errorBox = jQuery('.da-error').show();
if (errorBox.length) {
var contents = errorBox.html();
errorBox.html(contents + '<br/><br/><strong>Error:</strong> ' + errorMsg + '<br/>Line ' + lineNumber + ': ' + url);
}
return false;
}
/* Fix for Elementor bug - duplicating DA images when popup opens */
jQuery(document).on('elementor/popup/show', () => {
setTimeout(function(){
var imageContainers = jQuery('.hotspots-image-container');
imageContainers.each(function(){
var extraMapContainers = jQuery(this).find('.hotspots-map-container:not(:first)').remove(); // find all but the first one and remove them
});
}, 250);
} );