1;(function ($, hotspots, undefined) { 2 "use strict"; 3 4 var ua = window.navigator.userAgent, 5 isiOS = !!ua.match(/iPad/i) || !!ua.match(/iPhone/i), 6 isWebkit = !!ua.match(/WebKit/i), 7 isWebkitiOS = isiOS && isWebkit, 8 isMobileSafari = isiOS && isWebkit && !ua.match(/CriOS/i), 9 getBrowserVersion = function(){ 10 var temp, 11 M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || []; 12 13 M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, '-?']; 14 if ((temp = ua.match(/version\/(\d+)/i)) != null) { 15 M.splice(1, 1, temp[1]); 16 } 17 return parseInt(M[1]); 18 } 19 20 // Store all the leaflets on the page in an array for access later 21 var leaflets = []; 22 23 // Store all image maps on the page in an object for access later 24 var imageMaps = {}; 25 26 // Store all the more info area hotspots in an object for access later 27 hotspots.infoSpots = {}; 28 29 var mapSetup = function(){ 30 $('.da-error').hide(); 31 $('.hotspot-info').addClass('da-hidden'); 32 33 $('img.hotspots-image').each(function(){ 34 var img = $(this); 35 36 var tester = new Image(); 37 38 tester.onload = function(){ 39 markLoaded(img); 40 moreInfoSetup(img); 41 leafletSetup(img); 42 }; 43 tester.src = img.attr('src'); 44 }); 45 46 if (isMobileSafari) { 47 window.onpageshow = function(event){ 48 if (event.persisted) { 49 window.location.reload(); 50 } 51 }; 52 } 53 }; 54 55 var markLoaded = function(img) { 56 img.data('status', 'loaded'); 57 var container = img.parents('.hotspots-container').addClass('loaded'); 58 }; 59 60 var stopVideo = function(el) { 61 var iframe = el.querySelector( 'iframe[src*=youtube]'); 62 var video = el.querySelector( 'video' ); 63 if (iframe) { 64 var iframeSrc = iframe.src; 65 iframe.src = iframeSrc; 66 } 67 if (video) { 68 video.pause(); 69 } 70 } 71 72 var moreInfoSetup = function(img) { 73 var container = img.parents('.hotspots-container'); 74 75 if (container.data('layout') == 'tooltip') { 76 return; 77 } 78 79 if (container.data('layout') == 'lightbox') { 80 lightboxSetup(container, img); 81 return; 82 } 83 84 infoboxSetup(container, img); 85 }; 86 87 var infoboxSetup = function(container, img) { 88 var initial = container.find('.hotspot-initial'); 89 var content = container.find('.hotspots-placeholder'); 90 initial.addClass('visible'); 91 container.on('active.responsilight inactive.responsilight', function(e){ 92 var data = $(e.target).data('areaData'); 93 var info; 94 if (e.type === 'active') { 95 info = $(data.href); 96 } else { 97 info = initial; 98 } 99 100 var visibleContent = content.children('.visible'); 101 stopVideo(visibleContent.get(0)); 102 visibleContent.removeClass('visible'); 103 info.removeClass('da-hidden').addClass('visible').appendTo(content); 104 105 106 // Check for an embedded video player 107 var video = info.find('.wp-video'); 108 if (video.length) { 109 if (!info.data('video-resized')) { 110 info.data('video-resized', true); 111 window.dispatchEvent(new Event('resize')); 112 } 113 } 114 }); 115 }; 116 117 var lightboxSetup = function(container, img) { 118 container.on('active.responsilight', function(e){ 119 var data = $(e.target).data('areaData'), 120 info = $(data.href), 121 container = info.parent(), 122 target = $(e.target), 123 currentLightbox = $('.featherlight'); 124 125 if (e.type === 'active' && currentLightbox.length === 0) { 126 $.featherlight('<div class="hotspot-info"></div>', { 127 closeSpeed: 250, 128 closeOnEsc: false, 129 afterContent: function(){ 130 var content = $('.featherlight-inner'), 131 lb = $('.featherlight-content'), 132 mapNo = img.data('id'); 133 134 info.appendTo(content).show(); 135 lb.addClass('lightbox-' + mapNo); 136 137 // Fix accessibility issue - links not focusable by keyboard 138 var untabbables = info.find('a[tabindex=-1]'); 139 untabbables.attr('tabindex', 0); 140 141 setTimeout(function(){ 142 var img = content.find('img'), 143 contentHeight = content.get(0).scrollHeight, 144 imgHeight = img.height(), 145 lbHeight = lb.outerHeight(); 146 147 if (contentHeight > lbHeight) { 148 var diff = contentHeight - lbHeight + 50, 149 newHeight = imgHeight - diff, 150 minHeight = $(window).innerHeight()/2; 151 152 newHeight = newHeight < minHeight ? minHeight : newHeight; 153 154 var naturalHeight = img.prop('naturalHeight'); 155 156 if (newHeight < naturalHeight) { 157 img.css({'width': 'auto'}); 158 img.animate({ 159 'height': newHeight 160 }, 200); 161 } 162 } 163 }, 100); 164 165 lightboxAnchorLinks(content, target); 166 }, 167 afterOpen: function() { 168 $('body').on('keyup', documentEsc); 169 }, 170 afterClose: function(){ 171 target.removeClass('hotspot-active'); 172 $('body').off('keyup', documentEsc); 173 }, 174 beforeClose: function(){ 175 stopVideo(document.querySelector('.featherlight-content')); 176 info.hide().appendTo(container); 177 } 178 }); 179 } 180 }); 181 }; 182 183 var documentEsc = function(e) { 184 if (e.keyCode === 27) { 185 $.featherlight.current().close(); 186 } 187 }; 188 189 var lightboxAnchorLinks = function(content, hotspot){ 190 var links = content.find('.hotspot-content a[href^="#"]'); 191 links.on('click', function(e){ 192 e.preventDefault(); 193 var targetEl = $(e.target.hash); 194 var current = $.featherlight.current(); 195 if (!current) return 196 current.afterClose = function(){ 197 hotspot.removeClass('hotspot-active'); 198 $('html').removeClass('with-featherlight'); 199 $('html, body').animate({ 200 scrollTop: targetEl.offset().top 201 }, 500); 202 } 203 current.close(); 204 }); 205 }; 206 207 var showTooltip = function(shape, areaData) { 208 var container = $(shape._map._container); 209 var content = $(areaData.href).html(); 210 211 var tip = L.responsivePopup({ 212 autoPan: false, 213 closeButton: areaData.trigger == 'click', 214 hasTip: container.width() > 840, 215 maxHeight: container.height() * .9, 216 offset: new L.Point(0,0) 217 }); 218 219 tip.setContent(content); 220 shape.bindPopup(tip); 221 222 if (areaData.trigger === 'click') { 223 shape.on('click', function(e){ 224 if (shape._path.classList.contains('hotspot-active')) { 225 shape.closePopup() 226 } else { 227 shape.openPopup(); 228 } 229 }); 230 } else { 231 shape.on('mouseover', function(){ 232 shape.openPopup(); 233 }); 234 shape.on('mouseout', function(){ 235 shape.closePopup(); 236 }); 237 } 238 239 container.on('click', function(e){ 240 e.stopPropagation(); 241 }); 242 $(document).on('click', function(e){ 243 shape.closePopup(); 244 shape._path.classList.remove('hotspot-active'); 245 }); 246 }; 247 248 var leafletSetup = function(img) { 249 var id = img.data('id');
250 var container = $('<div id="hotspots-map-container-' + id + '" class="hotspots-map-container"></div>'); 251 var imgWidth = img.width(); 252 var imgHeight = img.height(); 253 254 container.css({ 255 'width': imgWidth + 'px', 256 'height': imgHeight + 'px' 257 }); 258 img.after(container); 259 260 // Check to be sure the container is on the page 261 // To prevent errors when loaded in Elementor popup 262 var containerTest = $('#hotspots-map-container-' + id); 263 if (!containerTest.length) { 264 return 265 } 266 267 var map = L.map('hotspots-map-container-' + id, { 268 attributionControl: false, 269 boxZoom: false, 270 crs: L.CRS.Simple, 271 doubleClickZoom: false, 272 dragging: false, 273 keyboard: false, 274 minZoom: -20, 275 scrollWheelZoom: false, 276 // tap: !isWebkitiOS, 277 tap: true, 278 touchZoom: false, 279 zoomControl: false, 280 zoomSnap: 0, 281 }); 282 283 var domImg = img.get(0); 284 var natHeight = domImg.naturalHeight; 285 var natWidth = domImg.naturalWidth; 286 img.data('natW', natWidth); 287 img.data('natH', natHeight); 288 var bounds = [[0,0], [natHeight, natWidth]]; 289 var imageLayer = L.imageOverlay(img.attr('src'), bounds).addTo(map); 290 map.fitBounds(bounds); 291 292 leaflets.push({ 293 map: map, 294 img: img 295 }); 296 297 drawSpots(img, map); 298 }; 299 300 var showContextMenu = function(shape, areaData, e) { 301 var hash = areaData.href; 302 var container = $(shape._map._container); 303 304 if (!hash) { 305 return; 306 } 307 308 $('.da-address-wrapper').remove(); 309 310 var windowAddress = window.location.href.split('#')[0]; 311 var shapeAddress = windowAddress + hash; 312 var div = $('<div class="da-address-wrapper"></div>') 313 .css({ 314 'left': e.originalEvent.pageX + 'px', 315 'top': e.originalEvent.pageY + 'px' 316 }) 317 .append('<p>' + shapeAddress + '</p>') 318 .append('<span class="da-address-close">×</span>') 319 .appendTo(document.body); 320 321 div.find('.da-address-close').on('click', function(){ 322 div.remove(); 323 }); 324 }; 325 326 var drawSpots = function(img, map) { 327 var id = img.data('id'); 328 329 /* If the image has a usemap attribute, detach the map and store it in the imageMaps object */ 330 if (img[0].hasAttribute('usemap')) { 331 var mapName = img.attr('usemap').replace('#', ''); 332 var imageMap = $('map[name="' + mapName + '"]'); 333 imageMaps[id] = imageMap.detach(); 334 img.removeAttr('usemap'); 335 } else { /* Else we've already removed the image map, so we just need to get it */ 336 var imageMap = imageMaps[id]; 337 } 338 339 var areas = imageMap.find('area'); 340 var container = img.parents('.hotspots-container'); 341 342 areas.each(function(){ 343 var area = $(this); 344 var shape = area.attr('shape'); 345 var coords = area.attr('coords').split(','); 346 var areaData = { 347 style: area.data('color-scheme') ? area.data('color-scheme') : 'default', 348 title: area.attr('title'), 349 href: area.attr('href'), 350 spot: area.data('id'), 351 target: area.attr('target'), 352 action: area.data('action'), 353 layout: container.data('layout'), 354 trigger: container.data('trigger') 355 }; 356 switch(shape) { 357 case 'circle': 358 renderCircle(coords, map, img, areaData); 359 break; 360 case 'rect': 361 renderPoly(coords, map, img, areaData); 362 break 363 case 'poly': 364 renderPoly(coords, map, img, areaData); 365 break; 366 } 367 }); 368 // A11y fixes after all the spots are drawn 369 a11yFixes(img, map); 370 371 // Link to area after all the spots are drawn 372 hotspots.linkToArea(); 373 }; 374 375 var renderCircle = function(coords, map, img, areaData) { 376 var x = coords[0]; 377 var y = img.data('natH') - coords[1]; 378 var rad = coords[2]; 379 var circle = L.circle([y,x], { 380 radius: rad, 381 className: 'hotspot-' + areaData.style, 382 title: areaData.title 383 }).addTo(map) 384 shapeEvents(circle, areaData); 385 }; 386 387 var renderPoly = function (coords, map, img, areaData) { 388 var xCoords = []; 389 var yCoords = []; 390 for (var i = 0; i < coords.length; i++) { 391 if (i % 2 == 0) { 392 xCoords.push(coords[i]); 393 } else { 394 yCoords.push(coords[i]); 395 } 396 } 397 398 var polyCoords = yCoords.map(function(coord, index) { 399 return [img.data('natH') - coord, xCoords[index]]; 400 }); 401 402 var poly = L.polygon(polyCoords, {
403 className: 'hotspot-' + areaData.style, 404 title: areaData.title 405 }); 406 407 // Attach the area data to the path as soon as it's added to the map, a11y attributes 408 poly.on('add', function(e) { 409 var $poly = $(e.target.getElement()); 410 $poly.data('areaData', areaData); 411 $poly.attr('tabindex', '0'); 412 $poly.attr('aria-label', areaData.title); 413 }); 414 415 poly.addTo(map); 416 417 // If this is a more info hotspot, add it to the infoSpots object 418 if (areaData.href.charAt(0) === '#') { 419 var spotName = areaData.href.replace('#', ''); 420 hotspots.infoSpots[spotName] = poly; 421 } 422 423 shapeEvents(poly, areaData); 424 }; 425 426 var shapeOver = function(shape, areaData, e) { 427 var $shape = $(e.target.getElement()); 428 $shape.trigger('over.responsilight'); 429 if (areaData.trigger === 'hover' && e.type !== 'touchstart' & e.type !== 'keypress') { 430 $shape.addClass('hotspot-active'); 431 $shape.trigger('active.responsilight'); 432 } 433 }; 434 435 var shapeOut = function(shape, areaData, e) { 436 var $shape = $(e.target.getElement()); 437 $shape.trigger('out.responsilight'); 438 if (areaData.trigger === 'hover' && e.type !== 'keypress') { 439 $shape.removeClass('hotspot-active'); 440 $shape.trigger('inactive.responsilight'); 441 } 442 }; 443 444 var shapeClick = function(shape, areaData, e) { 445 var $shape = $(e.target.getElement()); 446 $shape.trigger('areaClick.responsilight'); 447 if (areaData.trigger === 'hover' && e.type !== 'touchstart' && e.type !== 'keypress' && !isMobileSafari) { 448 return; 449 } 450 $shape.toggleClass('hotspot-active'); 451 if ($shape.hasClass('hotspot-active')) { 452 $shape.trigger('active.responsilight'); 453 } else { 454 $shape.trigger('inactive.responsilight'); 455 } 456 var oldActive = $shape.siblings('.hotspot-active'); 457 if (oldActive.length) { 458 oldActive.removeClass('hotspot-active'); 459 } 460 }; 461 462 var shapeEvents = function(shape, areaData) { 463 // Handle URL spots 464 if (areaData.action == 'url') { 465 if (areaData.title) { 466 shape.bindTooltip(areaData.title); 467 } 468 shape.on('click', function(e) { 469 if (areaData.target == '_new' && !isMobileSafari) { // new window 470 window.open(areaData.href, '_blank'); 471 } else { // same window 472 var first = areaData.href.charAt(0); 473 var targetElem; 474 try { 475 targetElem = first === '#' ? $(areaData.href) : null; 476 } catch (error) { 477 targetElem = null 478 } 479 if (targetElem && targetElem.length) { // hash link to existing target 480 $('html, body').animate({ 481 scrollTop: targetElem.offset().top - 50 482 }, 750, function(){ // callback after scrolling 483 history.pushState({}, '', areaData.href); 484 }) 485 } else { 486 window.location = areaData.href; 487 } 488 } 489 }); 490 return; 491 } 492 493 // Show right-click context menu for logged-in admins only 494 if (drawattentionData.isLoggedIn && drawattentionData.isAdmin) { 495 shape.on('contextmenu', function(e){ 496 showContextMenu(shape, areaData, e); 497 }); 498 } 499 500 // Handle tooltip spots 501 if (areaData.layout === 'tooltip') { 502 showTooltip(shape, areaData); 503 } 504 505 // Add styled tooltip to all non-hover areas 506 if (areaData.action === 'url' || areaData.trigger === 'click' && areaData.layout !== 'tooltip') { 507 if (areaData.title) { 508 shape.bindTooltip(areaData.title); 509 } 510 } 511 512 // Handle all other spots 513 var moved = false; 514 515 shape.on('touchstart touchmove touchend click mouseover mouseout keypress focus blur', function(e){ 516 switch(e.type) { 517 case 'touchstart': 518 moved = false; 519 break; 520 case 'touchmove': 521 moved = true; 522 break; 523 case 'touchend': 524 if (moved) { 525 return; 526 } 527 shapeOver(shape, areaData, e); 528 shapeClick(shape, areaData, e); 529 break; 530 case 'click': 531 shapeClick(shape, areaData, e); 532 break; 533 case 'keypress': 534 var key = e.originalEvent.which; 535 if ( key == 13 || key == 32) { 536 e.originalEvent.preventDefault(); 537 shapeClick(shape, areaData, e); 538 } 539 break; 540 case 'mouseover': 541 if (!isMobileSafari || (isMobileSafari && getBrowserVersion < 13)) { 542 shapeOver(shape, areaData, e); 543 } 544 if (isMobileSafari && !(getBrowserVersion() >= 12)) { 545 shapeClick(shape, areaData, e); 546 } 547 break; 548 case 'focus': 549 shapeOver(shape, areaData, e); 550 break; 551 case 'blur': 552 shapeOut(shape, areaData, e); 553 break; 554 case 'mouseout': 555 shapeOut(shape, areaData, e); 556 break; 557 } 558 }); 559 }; 560 561 var a11yFixes = function(img, map){ 562 let svg = img.siblings('.leaflet-container').find('.leaflet-overlay-pane svg'); 563 let id = img.data('id'); 564 565 // Add title and description to SVG 566 svg.prepend('<description id="img-desc-' + id + '">' + img.data('image-description') + '</description'); 567 svg.prepend('<title id="img-title-' + id + '">' + img.data('image-title') + '</title>'); 568 569 // Use title and desc to describe the svg 570 svg.attr('aria-labelled-by', 'img-title-' + id); 571 svg.attr('aria-described-by', 'img-desc-' + id); 572 573 // Make svg screen reader traversable 574 svg.attr('role', 'group'); 575 576 // Create a traversable list
577 var group = svg.find('g').attr('role', 'list'); 578 group.children().attr('role', 'listitem'); 579 580 // Handle tooltips for the map 581 map.on('popupopen',function(popup) { 582 // shift focus to the popup when it opens 583 $(popup.popup._container).find('.leaflet-rrose-content').attr('tabindex','-1').focus(); 584 585 // move the close button to the end of the popup content so screen readers reach it 586 // after the main popup content, not before 587 var close = $(popup.popup._container).find('.leaflet-rrose-close-button').detach(); 588 close.attr('aria-label','Close item'); 589 $(popup.popup._container).append(close); 590 591 $(document).on('keydown', function(e){ 592 if (e.which == 27) { 593 map.closePopup(); 594 } 595 }); 596 597 }); 598 599 // return focus to the icon we started from before opening the pop up 600 map.on('popupclose',function(popup) { 601 $(document).off('keydown'); 602 $(popup.popup._source._path).focus(); 603 }); 604 605 }; 606 607 hotspots.linkToArea = function(){ // Called after the shapes are drawn 608 var hash = window.location.hash, 609 area = null; 610 611 if (!hash) return; 612 613 var spotName = hash.replace('#', ''); 614 615 if (!hotspots.infoSpots.hasOwnProperty(spotName)) return; 616 617 hotspots.infoSpots[spotName].fire('click') 618 }; 619 620 hotspots.setup = function(){ 621 mapSetup(); 622 }; 623 624 hotspots.resizeTimer = null; 625 hotspots.resizing = false; 626 627 hotspots.init = function(){ // For backward compatibility - resets the size of the leaflet on demand 628 leaflets.forEach(function(item){ 629 var isLoaded = item.img.data('status') === 'loaded'; 630 631 if (!isLoaded) { 632 return; 633 } 634 635 item.img.next('.hotspots-map-container').css({ 636 'width': item.img.width() + 'px', 637 'height': item.img.height() + 'px' 638 }); 639 item.map.invalidateSize(true); 640 item.map.fitBounds([[0,0], [item.img.data('natH'), item.img.data('natW')]]); 641 }); 642 }; 643 644 hotspots.compatibilityFixes = function(){ 645 if (window.Foundation) { /* Fix for Foundation firing tag change event indiscriminately when some items are clicked on the page */ 646 $(window).on('change.zf.tabs', function(e){ 647 if (e.target.tagName !== 'INPUT') { 648 hotspots.init(); 649 } 650 }); 651 } 652 $(window).on('pageloaded', function(){ 653 hotspots.init(); 654 }); 655 $(window).on('load', function(){ 656 hotspots.init(); 657 }); 658 $('a[data-vc-accordion], .et_pb_tabs .et_pb_tabs_controls li, .et_pb_toggle_title').on('click', function() { 659 setTimeout(function() { 660 hotspots.init(); 661 }, 1000); 662 }); 663 $('.ui-tabs-anchor, .nav-tabs > li').on('click', function() { 664 setTimeout(function() { 665 hotspots.init(); 666 }, 750); 667 }); 668 $('.responsive-tabs').on('click', '.responsive-tabs__list__item', function(){ 669 hotspots.init(); 670 }); 671 $(window).on('et_hashchange', function() { 672 setTimeout(function() { 673 hotspots.init(); 674 }, 1000); 675 }); 676 $('.vc_tta-tabs-container').on('click', '.vc_tta-tab', function(){ 677 setTimeout(function() { 678 hotspots.init(); 679 }, 1000); 680 }); 681 $('.elementor-tabs').on('click', '.elementor-tab-title', function(){ 682 hotspots.init(); 683 }); 684 685 jQuery('.ult_tabs').on('click', '.ult_tab a', function(){ 686 setTimeout(function() { 687 hotspots.init(); 688 }, 2000); 689 }); 690 }; 691 692}(jQuery, window.hotspots = window.hotspots || {})); 693 694jQuery(function(){ 695 hotspots.setup(); 696 hotspots.compatibilityFixes(); 697}); 698 699jQuery(document).on('elementor/popup/show', function(){ 700 hotspots.setup(); 701 hotspots.compatibilityFixes(); 702}); 703 704jQuery(window).on('resize orientationchange', function(e){ 705 var $window = jQuery(this); 706 if (!hotspots.resizing) { 707 $window.trigger('resizeStart.responsilight'); 708 hotspots.resizing = true; 709 } 710 clearTimeout(hotspots.resizeTimer); 711 hotspots.resizeTimer = setTimeout(function(){ 712 hotspots.init(); 713 $window.trigger('resizeComplete.responsilight'); 714 hotspots.resizing = false; 715 }, 250); 716}); 717 718jQuery(window).on('hashchange', function(){ 719 hotspots.linkToArea(); 720}); 721 722window.onerror = function(errorMsg, url, lineNumber) { // This should be a fun little experiement! 723 var errorBox = jQuery('.da-error').show(); 724 if (errorBox.length) { 725 var contents = errorBox.html(); 726 errorBox.html(contents + '<br/><br/><strong>Error:</strong> ' + errorMsg + '<br/>Line ' + lineNumber + ': ' + url); 727 } 728 return false;
vendor: 1 bytes, line 729
729}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.