1/* 2 * Mapplic - Custom Interactive Map Plugin by @sekler 3 * Version 7.0 4 * Custom Version 0.1.0 5 * https://www.mapplic.com/ 6 */ 7 8(function ($) { 9 "use strict"; 10 var Mapplic = function (element) { 11 var self = this; 12 13 self.o = { 14 source: "locations.json", 15 selector: "[id^=MLOC] > *, [id^=landmark] > *, svg > #items > *", 16 external: ".mapplic-external", 17 scope: null, 18 customcss: null, 19 iconfile: null, 20 height: "auto", 21 minheight: 400, 22 maxheight: 1100, 23 csv: false, 24 landmark: false, 25 portrait: 860, 26 minimap: false, 27 deeplinking: true, 28 lightbox: true, 29 fullscreen: false, 30 hovertip: true, 31 defaultstyle: null, 32 moretext: null, 33 action: "default", 34 marker: "", 35 developer: false, 36 smartip: false, 37 animations: false, 38 hovertipdesc: false, 39 wayfinder: null, 40 41 // sidebar 42 sidebar: true, 43 sidebartoggle: false, 44 search: true, 45 searchlength: 1, 46 searchfields: ["title", "about", "category"], 47 searchdescription: false, 48 highlight: true, 49 thumbholder: false, 50 autopopulate: false, 51 sortby: "title", 52 53 // zoom 54 zoom: true, 55 clearbutton: true, 56 zoombuttons: true, 57 zoomoutclose: false, 58 closezoomout: true, 59 linknewtab: false, 60 mousewheel: true, 61 mapfill: false, 62 zoommargin: 200, 63 maxscale: 3, 64 65 // UI Colors 66 basecolor: null, 67 bgcolor: null, 68 bgcolor2: null, 69 headingcolor: null, 70 textcolor: null, 71 accentcolor: null, 72 }; 73 74 self.loc = { 75 more: "More", 76 search: self.o.searchPlaceHolder, 77 zoomin: "Zoom in", 78 zoomout: "Zoom out", 79 resetzoom: "Reset zoom", 80 levelup: "Level up", 81 leveldown: "Level down", 82 clearsearch: "Clear search", 83 closepopup: "Close popup", 84 clearfilter: "Clear filter", 85 iconfile: "/mapplic/images/icons.svg", 86 }; 87 88 self.el = element; 89 90 self.init = function (options) { 91 // merging options with defaults 92 self.o = $.extend(self.o, options); 93 if (typeof mapplic_localization !== "undefined") 94 self.loc = $.extend(self.loc, mapplic_localization); 95 if (self.o.iconfile) self.loc.iconfile = self.o.iconfile; 96 97 self.el.addClass("mapplic-element mapplic-loading"); 98 99 // scope 100 if (self.o.scope) self.scope = $(self.o.scope); 101 else self.scope = self.el; 102 103 // process map data 104 var data = self.el.data("mapdata"); 105 if (self.el.data("mapdata")) { 106 var mapdata = self.el.data("mapdata"); 107 self.el.removeAttr("data-mapdata").removeData("mapdata"); 108 processData(mapdata); 109 self.el.removeClass("mapplic-loading"); 110 } else if (typeof self.o.source === "string") { 111 // loading .json file with AJAX 112 $.getJSON(self.o.source, function (data) { 113 processData(data); 114 self.el.removeClass("mapplic-loading"); 115 }).fail(function () { 116 // Failure: couldn't load JSON file or it is invalid. 117 console.error( 118 "Couldn't load map data. (Make sure to run the script through web server)" 119 ); 120 self.el.removeClass("mapplic-loading").addClass("mapplic-error"); 121 // alert( 122 // "Data file missing or invalid!\nMake sure to run the script through web server." 123 // ); 124 }); 125 } else { 126 // inline json object 127 processData(self.o.source); 128 self.el.removeClass("mapplic-loading"); 129 } 130 131 return self; 132 }; 133 134 // wayfinder 135 function Wayfinder() { 136 this.param = "pin"; 137 this.pin = null; 138 139 this.init = function () { 140 if (typeof window.URL !== "undefined") { 141 const url = new URL(window.location.href); 142 this.pin = url.searchParams.get(this.param); 143 } 144 145 // add dynamic screen size UI scalling 146 const scale = window.innerWidth / 1920; 147 148 self.sidebar.el.css("transform", `scale(${scale})`); 149 self.sidebar.el.css("height", `calc(100% * (1 / ${scale}))`); 150 151 self.container.el.css("width", `calc(100% - (380px * ${scale}))`); 152 self.container.el 153 .find(".mapplic-level-switcher") 154 .css("transform", `scale(calc(1.5 * ${scale}))`); 155 156 return this; 157 }; 158 159 this.update = function () { 160 $('[id="wayfinders"]').children().hide(); 161 if (this.pin) $(`#${this.pin}`).show(); 162 }; 163 } 164 165 // tooltip 166 function Tooltip() { 167 this.el = null; 168 this.pin = null; 169 this.shift = 6; 170 this.drop = 0; 171 this.location = null; 172 173 this.init = function (location, check) { 174 var s = this; 175 176 // markup 177 this.el = $("<div></div>").addClass("mapplic-tooltip"); 178 this.wrap = $("<div></div>").addClass("mapplic-tooltip-wrap").appendTo(this.el); 179 this.close = $("<button></button>") 180 .append(getIcon("icon-cross")) 181 .addClass("mapplic-tooltip-close") 182 .attr("aria-label", self.loc.closepopup) 183 .on("click touchend", function (e) { 184 e.preventDefault(); 185 self.hideLocation(); 186 if (!self.o.zoom || self.o.zoomoutclose) 187 self.moveTo(0.5, 0.5, self.fitscale, 400); 188 }) 189 .appendTo(this.wrap); 190 if (location?.thumbnail) 191 this.image = $("<img>") 192 .addClass("mapplic-image") 193 .attr("alt", "Location image") 194 .hide() 195 .appendTo(this.wrap); 196 this.body = $("<div></div>") 197 .addClass("mapplic-tooltip-body") 198 .attr("aria-modal", "true") 199 .appendTo(this.wrap); 200 if (!location?.thumbnail) { 201 this.title = $("<h4></h4>") 202 .addClass("mapplic-tooltip-title") 203 .appendTo(this.body); 204 } 205 this.content = $("<div></div>") 206 .addClass("mapplic-tooltip-content") 207 .appendTo(this.body); 208 this.desc = $("<div></div>") 209 .addClass("mapplic-tooltip-description") 210 .appendTo(this.content); 211 this.link = $("<a>" + self.loc.more + "</a>") 212 .addClass("mapplic-popup-link") 213 .attr("href", "#") 214 .hide() 215 .appendTo(this.body); 216 if (self.o.linknewtab) this.link.attr("target", "_blank"); 217 this.triangle = $("<div></div>") 218 .addClass("mapplic-tooltip-triangle") 219 .prependTo(this.wrap); 220 221 // no focus on deeplink 222 if (!check) this.body.attr("tabindex", "-1"); 223 224 $(".mapplic-layer.mapplic-visible", self.map).append(this.el); 225 226 if (self.o.smartip) self.el.on("positionchanged", { s: s }, this.repos); 227 228 this.el.css({ transform: "scale(" + 1 / self.scale + ")" }); 229 230 if (location) this.show(location); 231 232 // close tooltip 233 $(document).on("keyup.mapplic", function (e) { 234 e.stopImmediatePropagation(); 235 if (e.keyCode === 27) { 236 self.hideLocation(); 237 if (!self.o.zoom || self.o.zoomoutclose) 238 self.moveTo(0.5, 0.5, self.fitscale, 400); 239 } 240 }); 241 242 return this; 243 }; 244 245 this.repos = function (e) { 246 var s = e.data.s, 247 tx = "-50%", 248 ty = "-100%"; 249 250 // vertical 251 var rtop = s.el.offset().top - self.container.el.offset().top; 252 if (rtop < s.wrap.outerHeight() + 36) { 253 s.el.addClass("mapplic-tooltip-bottom"); 254 ty = "0%"; 255 } else { 256 s.el.removeClass("mapplic-tooltip-bottom"); 257 ty = "-100%"; 258 } 259 260 // horizontal 261 var rleft = s.el.offset().left - self.container.el.offset().left; 262 if (rleft < s.wrap.outerWidth() / 2) { 263 if (rleft > 12) tx = -(100 + (rleft / s.wrap.outerWidth()) * 100) + 100 + "%"; 264 else tx = "-10%"; 265 } else if (rleft > self.container.el.outerWidth() - s.wrap.outerWidth() / 2) { 266 if (rleft < self.container.el.outerWidth() - 12) 267 tx = 268 ((self.container.el.outerWidth() - rleft) / s.wrap.outerWidth()) * 100 - 269 100 + 270 "%"; 271 else tx = "-90%"; 272 } else tx = "-50%"; 273 274 s.wrap.css({ transform: "translate(" + tx + ", " + ty + ")" }); 275 }; 276 277 this.show = function (location) { 278 if (location) { 279 var s = this; 280 281 this.el.attr("data-location", location.id); 282 283 this.location = location; 284 if (self.hovertip) self.hovertip.hide(); 285 286 if (location.image) { 287 this.image.attr("src", ""); 288 this.image.attr("src", location.image).show(); 289 } else this.image?.hide(); 290 291 if (location.link) { 292 this.link.attr("href", location.link).css("background-color", "").show(); 293 if (location.color) this.link.css("background-color", location.color); 294 } else this.link.hide(); 295 if (!location?.thumbnail) this.title.text(location.title); 296 if (location.description) this.desc.html(location.description); 297 else this.desc.empty(); 298 this.content[0].scrollTop = 0; 299 300 // shift 301 this.pin = $('.mapplic-pin[data-location="' + location.id + '"]'); 302 if (this.pin.length == 0) { 303 this.shift = 20; 304 } else this.shift = Math.abs(parseFloat(this.pin.css("margin-top"))) + 20; 305 306 // making it visible 307 this.el.stop().css({ opacity: 1 }).show(); 308 setTimeout(function () { 309 s.body.focus(); 310 }, 600); 311 this.position(); 312 if (self.o.zoom) this.zoom(location); 313 314 // loading & positioning 315 $("img", this.el) 316 .off("load") 317 .on("load", function () { 318 s.position(); 319 if (self.o.zoom) s.zoom(location); 320 }); 321 } 322 }; 323 324 this.position = function () { 325 if (this.location) { 326 this.el.css({ 327 left: this.location.x * 100 + "%", 328 top: this.location.y * 100 + "%", 329 }); 330 this.drop = this.el.outerHeight() + this.shift; 331 if (self.o.smartip) this.repos({ data: { s: this } }); 332 } 333 }; 334 335 this.zoom = function (location) { 336 var ry = 0.5, 337 zoom = location.zoom ? parseFloat(location.zoom) / self.o.maxscale : 1; 338 339 ry = 340 (self.container.el.height() / 2 + this.drop / 2) / self.container.el.height(); 341 self.moveTo(location.x, location.y, zoom, 600, ry); 342 }; 343 344 this.hide = function () { 345 var s = this; 346 347 this.location = null; 348 this.el.stop().fadeOut(200, function () { 349 $(this).remove(); 350 }); 351 if (self.o.smartip) self.el.off("positionchanged", this.repos); 352 }; 353 } 354 355 // hover tooltip 356 function HoverTooltip() { 357 this.el = null; 358 this.pin = null; 359 this.shift = 6; 360 361 this.init = function () { 362 var s = this; 363 364 // construct 365 this.el = $("<div></div>").addClass("mapplic-tooltip mapplic-hovertip"); 366 this.wrap = $("<div></div>").addClass("mapplic-tooltip-wrap").appendTo(this.el); 367 this.title = $("<h4></h4>").addClass("mapplic-tooltip-title").appendTo(this.wrap); 368 if (self.o.hovertipdesc) 369 this.desc = $("<div></div>") 370 .addClass("mapplic-tooltip-description") 371 .appendTo(this.wrap); 372 this.triangle = $("<div></div>") 373 .addClass("mapplic-tooltip-triangle") 374 .appendTo(this.wrap); 375 376 // events 377 // markers + old svg 378 $(self.map) 379 .on("mouseover", ".mapplic-pin", function () { 380 var id = $(this).data("location"); 381 382 s.pin = $('.mapplic-pin[data-location="' + id + '"]'); 383 s.shift = Math.abs(parseFloat(s.pin.css("margin-top"))) + 20; 384 385 var location = self.l[id]; 386 if (location && location.title) s.show(location); 387 }) 388 .on("mouseout", function () { 389 s.hide(); 390 }); 391 392 // new svg 393 if (self.o.selector) { 394 $(self.map) 395 .on("mouseover touchstart", self.o.selector, function () { 396 var location = self.l[$(this).attr("id")]; 397 s.shift = 20; 398 if (location && location.title) s.show(location); 399 }) 400 .on("mouseout touchend", function () { 401 s.hide(); 402 }); 403 } 404 405 self.el.on("positionchanged", { s: s }, this.repos); 406 407 self.map.append(this.el); 408 409 return this; 410 }; 411 412 this.repos = function (e) { 413 var s = e.data.s, 414 tx = "-50%", 415 ty = "-100%"; 416 417 // vertical 418 var rtop = s.el.offset().top - self.container.el.offset().top; 419 if (rtop < s.wrap.outerHeight() + 36) { 420 s.el.addClass("mapplic-tooltip-bottom"); 421 ty = "0%"; 422 } else { 423 s.el.removeClass("mapplic-tooltip-bottom"); 424 ty = "-100%"; 425 } 426 427 // horizontal 428 var rleft = s.el.offset().left - self.container.el.offset().left; 429 if (rleft < s.wrap.outerWidth() / 2) { 430 if (rleft > 12) tx = -(100 + (rleft / s.wrap.outerWidth()) * 100) + 100 + "%"; 431 else tx = "-10%"; 432 } else if (rleft > self.container.el.outerWidth() - s.wrap.outerWidth() / 2) { 433 if (rleft < self.container.el.outerWidth() - 12) 434 tx = 435 ((self.container.el.outerWidth() - rleft) / s.wrap.outerWidth()) * 100 - 436 100 + 437 "%"; 438 else tx = "-90%"; 439 } else tx = "-50%"; 440 441 s.wrap.css({ transform: "translate(" + tx + ", " + ty + ")" }); 442 }; 443 444 this.show = function (location) { 445 if (self.location != location) { 446 this.title.html( 447 `${ 448 location.thumbnail 449 ? `<img style="width:80px; height:80px" src="${location.thumbnail}"/>` 450 : `<h4>${location.title}` 451 }` 452 ); 453 if (self.o.hovertipdesc) this.desc.html(location.description); 454 this.position(location); 455 456 this.el.stop().fadeIn(100); 457 } 458 this.repos({ data: { s: this } }); 459 }; 460 461 this.position = function (location) { 462 if (location) { 463 this.el.css({ 464 left: location.x * 100 + "%", 465 top: location.y * 100 + "%", 466 }); 467 468 this.drop = this.el.outerHeight() + this.shift; 469 } 470 }; 471 472 this.hide = function () { 473 this.el.stop().fadeOut(200); 474 }; 475 } 476 477 // lightbox 478 function Lightbox() { 479 this.el = null; 480 481 this.init = function () { 482 // construct 483 this.el = $("<div></div>").addClass("mapplic-lightbox mfp-hide"); 484 this.title = $("<h4></h4>").addClass("mapplic-lightbox-title").appendTo(this.el); 485 this.desc = $("<div></div>") 486 .addClass("mapplic-lightbox-description") 487 .appendTo(this.el); 488 this.link = $("<a>" + self.loc.more + "</a>") 489 .addClass("mapplic-popup-link") 490 .attr("href", "#") 491 .hide() 492 .appendTo(this.el); 493 if (self.o.linknewtab) this.link.attr("target", "_blank"); 494 495 // append 496 self.el.append(this.el); 497 498 return this; 499 }; 500 501 this.show = function (location) { 502 this.location = location; 503 504 this.title.text(location.title); 505 this.desc.html(location.description); 506 showImage(); 507 if (location.link) { 508 this.link.attr("href", location.link).css("background-color", "").show(); 509 if (location.color) this.link.css("background-color", location.color); 510 } else this.link.hide(); 511 512 var s = this; 513 514 $.magnificPopup.open({ 515 items: { src: this.el }, 516 type: "inline", 517 removalDelay: 300, 518 mainClass: "mfp-fade", 519 callbacks: { 520 beforeClose: function () { 521 s.hide(); 522 }, 523 }, 524 }); 525 526 // zoom 527 var zoom = location.zoom ? parseFloat(location.zoom) : 1; 528 if (self.o.zoom) self.moveTo(location.x, location.y, zoom, 600); 529 530 return this.el[0]; 531 }; 532 533 this.showImage = function (location) { 534 this.location = location; 535 536 var s = this; 537 538 $.magnificPopup.open({ 539 items: { src: location.image }, 540 ty
540pe: "image", 541 removalDelay: 300, 542 mainClass: "mfp-fade", 543 callbacks: { 544 beforeClose: function () { 545 s.hide(); 546 }, 547 }, 548 }); 549 550 // zoom 551 var zoom = location.zoom ? parseFloat(location.zoom) : 1; 552 if (self.o.zoom) self.moveTo(location.x, location.y, zoom, 600); 553 }; 554 555 this.hide = function () { 556 this.location = null; 557 self.hideLocation(); 558 if (!self.o.zoom || self.o.zoomoutclose) 559 self.moveTo(0.5, 0.5, self.fitscale, 400); 560 }; 561 } 562 563 // external 564 function External() { 565 this.el = null; 566 567 this.init = function () { 568 this.el = $("<div></div>").addClass("mapplic-external-content").hide(); 569 this.title = $("<h4></h4>").addClass("mapplic-external-title").appendTo(this.el); 570 this.desc = $("<div></div>") 571 .addClass("mapplic-external-description") 572 .appendTo(this.el); 573 574 this.initial = $(self.o.external + " > *:not(.mapplic-external-content)"); 575 576 $(self.o.external).append(this.el); 577 578 return this; 579 }; 580 581 this.show = function (location) { 582 this.title.text(location.title); 583 this.desc.html(location.description); 584 585 this.initial.hide(); 586 this.el.show(); 587 }; 588 589 this.hide = function () { 590 this.initial.show(); 591 this.el.hide(); 592 }; 593 } 594 595 // deeplinking 596 function Deeplinking() { 597 this.param = "location"; 598 this.resolved = false; 599 600 this.init = function () { 601 var s = this; 602 603 window.onpopstate = function (e) { 604 if (e.state) s.check(600); 605 return false; 606 }; 607 }; 608 609 this.check = function (duration) { 610 var id = this.getUrlParam(this.param); 611 if (id) { 612 self.showLocation(id, duration, true); 613 this.resolved = true; 614 } 615 }; 616 617 this.getUrlParam = function (name) { 618 name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); 619 var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), 620 results = regex.exec(location.search); 621 return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); 622 }; 623 624 this.update = function (id) { 625 // var url; 626 // if (typeof window.URL !== "undefined") { 627 // url = new URL(window.location.href); 628 // url.searchParams.set(this.param, id); 629 // url = url.href; 630 // } else { 631 // url = 632 // window.location.protocol + 633 // "//" + 634 // window.location.host + 635 // window.location.pathname + 636 // "?" + 637 // this.param + 638 // "=" + 639 // id; 640 // } 641 // window.history.pushState({ path: url }, "", url); 642 }; 643 644 this.clear = function () { 645 // var url; 646 // if (typeof window.URL !== "undefined") { 647 // url = new URL(window.location.href); 648 // url.searchParams.delete(this.param); 649 // url = url.href; 650 // } else { 651 // url = window.location.pathname; 652 // } 653 // history.pushState("", document.title, url); 654 }; 655 } 656 657 // old hash deeplinking method for old browsers (starting IE9) 658 function DeeplinkingHash() { 659 this.param = "location"; 660 this.resolved = false; 661 662 this.init = function () { 663 var s = this; 664 this.check(0); 665 666 $(window).on("hashchange", function () { 667 //s.check(600); 668 }); 669 }; 670 671 this.check = function (duration) { 672 var id = location.hash.slice(this.param.length + 2); 673 if (id) { 674 self.showLocation(id, duration, true); 675 this.resolved = true; 676 } 677 }; 678 679 this.update = function (id) { 680 window.location.hash = this.param + "-" + id; 681 }; 682 683 this.clear = function () { 684 window.location.hash = this.param; 685 }; 686 } 687 688 // minimap 689 function Minimap() { 690 this.el = null; 691 this.opacity = null; 692 693 this.init = function () { 694 this.el = $("<div></div>") 695 .addClass("mapplic-minimap") 696 .appendTo(self.container.el); 697 this.el.click(function (e) { 698 e.preventDefault(); 699 700 var x = (e.pageX - $(this).offset().left) / $(this).width(), 701 y = (e.pageY - $(this).offset().top) / $(this).height(); 702 703 self.moveTo(x, y, self.scale / self.fitscale, 100); 704 }); 705 706 return this; 707 }; 708 709 this.addLayer = function (data) { 710 var layer = $("<div></div>") 711 .addClass("mapplic-minimap-layer") 712 .attr("data-floor", data.id) 713 .appendTo(this.el), 714 s = this; 715 716 $("<img>") 717 .attr("src", data.minimap) 718 .addClass("mapplic-minimap-background") 719 .attr("aria-hidden", "true") 720 .appendTo(layer); 721 $("<div></div>").addClass("mapplic-minimap-overlay").appendTo(layer); 722 $("<img>") 723 .attr("src", data.minimap) 724 .addClass("mapplic-minimap-active") 725 .attr("aria-hidden", "true") 726 .on("load", function () { 727 s.update(); 728 }) 729 .appendTo(layer); 730 }; 731 732 this.show = function (target) { 733 $(".mapplic-minimap-layer", this.el).hide(); 734 $('.mapplic-minimap-layer[data-floor="' + target + '"]', this.el).show(); 735 }; 736 737 this.update = function (x, y) { 738 var active = $(".mapplic-minimap-active", this.el); 739 740 if (x === undefined) x = self.x; 741 if (y === undefined) y = self.y; 742 743 var width = 744 (self.container.el.width() / self.contentWidth / self.scale) * 745 this.el.width(), 746 height = 747 (self.container.el.height() / self.contentHeight / self.scale) * 748 this.el.height(), 749 top = (-y / self.contentHeight / self.scale) * this.el.height(), 750 left = (-x / self.contentWidth / self.scale) * this.el.width(), 751 right = left + width, 752 bottom = top + height; 753 754 active.each(function () { 755 $(this)[0].style.clip = 756 "rect(" + top + "px, " + right + "px, " + bottom + "px, " + left + "px)"; 757 }); 758 759 // fade out effect
760 var s = this; 761 this.el.show(); 762 this.el.css("opacity", 1.0); 763 clearTimeout(this.opacity); 764 this.opacity = setTimeout(function () { 765 s.el.css("opacity", 0); 766 setTimeout(function () { 767 s.el.hide(); 768 }, 600); 769 }, 2000); 770 }; 771 } 772 773 // legend 774 function Legend() { 775 this.el = null; 776 this.nr = 0; 777 778 this.init = function () { 779 this.el = $("<div></div>").addClass("mapplic-legend"); 780 return this; 781 }; 782 783 this.build = function (categories) { 784 var s = this; 785 $.each(categories, function (index, category) { 786 if (category.legend == "true") s.add(category); 787 }); 788 if (this.nr > 0) this.el.appendTo(self.el); 789 }; 790 791 this.add = function (group) { 792 var toggle = this.newToggle(group, true); 793 if (toggle) toggle.appendTo(this.el); 794 else { 795 var key = $("<span></span>").addClass("mapplic-legend-key"); 796 if (group.color) key.css("background-color", group.color); 797 $("<span></span>") 798 .addClass("mapplic-legend-label") 799 .text(group.title) 800 .prepend(key) 801 .appendTo(this.el); 802 } 803 this.nr++; 804 }; 805 806 this.toggle = function (group, state) { 807 $('*[id="' + group + '"]', self.map).toggle(state); 808 $('*[data-category*="' + group + '"]', self.map).each(function () { 809 var attr = $(this).attr("data-category"); 810 if (attr == group) $(this).toggle(state); 811 else { 812 // multi-group support 813 var groups = attr.split(","), 814 show = false; 815 groups.forEach(function (g) { 816 if ($('.mapplic-toggle > input[data-group="' + g + '"]')[0].checked) 817 show = true; 818 }); 819 $(this).toggle(show); 820 } 821 }); 822 }; 823 824 this.newToggle = function (group, title) { 825 var s = this, 826 toggle = null; 827 828 if (group.toggle == "true") { 829 toggle = $('<label class="mapplic-toggle"></label>'); 830 var input = $('<input type="checkbox" checked>') 831 .attr("data-group", group.id) 832 .appendTo(toggle); 833 var circle = $("<span></span>") 834 .addClass("mapplic-toggle-circle") 835 .appendTo(toggle); 836 if (title) 837 $("<span></span>") 838 .addClass("mapplic-legend-label") 839 .text(group.title) 840 .appendTo(toggle); 841 if (group.switchoff == "true") input.prop("checked", false); 842 843 input.change(function () { 844 $('.mapplic-toggle > input[data-group="' + group.id + '"]', self.el).prop( 845 "checked", 846 this.checked 847 ); 848 s.toggle(group.id, this.checked); 849 }); 850 } 851 return toggle; 852 }; 853 854 this.applyToggles = function () { 855 var s = this; 856 $(".mapplic-toggle > input", self.el).each(function () { 857 s.toggle($(this).attr("data-group"), this.checked); 858 }); 859 }; 860 } 861 862 // groups 863 function Groups() { 864 this.init = function () {}; 865 866 this.addGroups = function (groups) { 867 if (groups) { 868 $.each(groups, function (index, group) { 869 self.g[group.id] = group; 870 if (group.style && self.s[group.style] && self.s[group.style].base) 871 group.color = self.s[group.style].base.fill; // overwrite color with style 872 }); 873 } 874 875 if (self.o.sidebar) self.sidebar.addCategories(groups); 876 }; 877 } 878 879 // directory 880 function Directory() { 881 this.dirs = []; 882 this.filters = {}; 883 884 this.init = function () { 885 var s = this, 886 event = "mapready"; 887 888 if (self.o.csv) event = "csvready"; 889 890 self.el.on(event, function (e, self) { 891 // dir 892 $(".mapplic-dir:not(.mapplic-dir-results)").each(function () { 893 var attribute = $(this).data("attribute"), 894 pattern = $(this).data("pattern"), 895 title = $(this).text(); 896 897 if (title) $("<h3><span>" + title + "</span></h3>").appendTo($(this).empty()); 898 s.getDirectory(attribute, pattern, attribute).appendTo($(this)); 899 }); 900 901 // filter 902 $(".mapplic-dir-filter").each(function () { 903 var el = $(this), 904 attribute = el.data("attribute"), 905 options = []; 906 907 // generate options 908 if ($(this).hasClass("mapplic-dir-filter-generate")) { 909 $.each(self.l, function (id, location) { 910 if (location[attribute]) { 911 var atts = location[attribute].toString().split(",");
912 atts.forEach(function (att) { 913 if (options.indexOf(att) === -1) options.push(att); 914 }); 915 } 916 }); 917 918 options.forEach(function (option) { 919 var text = option; 920 921 if (attribute == "category" && self.g[option]) 922 text = self.g[option].title; 923 $("<option></option>").attr("value", option).text(text).appendTo(el); 924 }); 925 } 926 927 // changed 928 $(this).on("change", function () { 929 if (this.value) s.filters[attribute] = this.value; 930 else delete s.filters[attribute]; 931 932 s.search(); 933 }); 934 }); 935 }); 936 937 // search 938 $(".mapplic-dir-search").keyup(function (e) { 939 var val = $(this).val(); 940 941 if (val) s.filters["keyword"] = val; 942 else delete s.filters["keyword"]; 943 944 s.search(); 945 946 if (e.keyCode == 13) $(".mapplic-dir-results li > a").filter(":first").click(); 947 else if (e.keyCode == 27) s.clearFilters(); 948 }); 949 950 // results 951 var restitle = $("<h3><span>0</span> found</h3>").appendTo( 952 $(".mapplic-dir-results") 953 ); 954 $("<ul></ul>").appendTo($(".mapplic-dir-results")); 955 $("<button></button>") 956 .text("Clear") 957 .append(getIcon("icon-cross")) 958 .addClass("mapplic-dir-results-clear") 959 .appendTo(restitle) 960 .on("click", function () { 961 s.clearFilters(); 962 }); 963 964 // list/grid 965 var directories = $(".mapplic-dir-view").parent().find(".mapplic-dir"); 966 $(".mapplic-dir-view-list").click(function () { 967 $(this).hide(); 968 $(".mapplic-dir-view-grid").show(); 969 directories.removeClass("mapplic-dir-grid"); 970 }); 971 $(".mapplic-dir-view-grid").click(function () { 972 $(this).hide(); 973 $(".mapplic-dir-view-list").show(); 974 directories.addClass("mapplic-dir-grid"); 975 }); 976 977 return this; 978 }; 979 980 this.getDirectory = function (attribute, pattern, sortby) { 981 var s = this, 982 dir = $("<ul></ul>"), 983 regex = new RegExp(pattern, "i"); 984 $.each(self.l, function (id, location) { 985 if (attribute && pattern && !regex.test(location[attribute])) return true; // skip if no match 986 if (String(location.hide) != "true") dir.append(s.getItem(location)); 987 }); 988 989 if (sortby) this.sortby(dir, sortby); 990 991 this.dirs.push(dir); 992 993 return dir; 994 }; 995 996 this.getItem = function (location) { 997 if (!location.id) location = self.l[location]; 998 999 var item = $("<li></li>") 1000 .addClass("mapplic-dir-item") 1001 .attr("data-location", location.id); 1002 var link = $("<a></a>") 1003 .attr("href", "#") 1004 .click(function (e) { 1005 e.preventDefault(); 1006 self.showLocation(location.id, 600); 1007 1008 // scroll back to map on mobile 1009 if ( 1010 $(window).width() < 668 && 1011 (location.action || self.o.action) != "lightbox" 1012 ) { 1013 $("html, body").animate( 1014 { 1015 scrollTop: self.container.el.offset().top, 1016 }, 1017 400 1018 ); 1019 } 1020 }) 1021 .appendTo(item); 1022 1023 if (self.o.sortby) item.data("sort", location[self.o.sortby]); 1024 1025 if (self.o.thumbholder) { 1026 var thumbnail = this.thumbnail(location.title, location.thumbnail); 1027 if (thumbnail) thumbnail.appendTo(link); 1028 } 1029 1030 $("<h4></h4>").text(location.title).appendTo(link); 1031 $("<span></span>").html(location.about).addClass("mapplic-about").appendTo(link); 1032 if (location.color) item.css("border-color", location.color); 1033 1034 location.list = item; 1035 1036 return item; 1037 }; 1038 1039 this.thumbnail = function (name, field) { 1040 var elem = null; 1041 1042 if (field) { 1043 if ( 1044 field.match( 1045 /(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/g 1046 ) 1047 ) 1048 elem = $("<img>") 1049 .attr("src", field) 1050 .attr("alt", name) 1051 .addClass("mapplic-thumbnail"); 1052 else 1053 elem = $("<div></div>") 1054 .addClass("mapplic-thumbnail mapplic-thumbnail-placeholder") 1055 .attr("aria-hidden", "true") 1056 .html(field); 1057 } else if (name) { 1058 var words = name.split(" "), 1059 text = ""; 1060
1061 if (words[0]) text += words[0][0]; 1062 if (words[1]) text += words[1][0]; 1063 elem = $("<div></div>") 1064 .addClass("mapplic-thumbnail mapplic-thumbnail-placeholder") 1065 .attr("aria-hidden", "true") 1066 .text(text.toUpperCase()); 1067 } 1068 1069 return elem; 1070 }; 1071 1072 this.search = function () { 1073 var s = this; 1074 1075 $(".mapplic-dir-results ul").empty(); 1076 1077 if (self.o.highlight) { 1078 self.map.removeClass("mapplic-filtered"); 1079 $(".mapplic-highlight", self.map).removeClass("mapplic-highlight"); 1080 } 1081 1082 $.each(self.l, function (i, location) { 1083 var matched = true; // matched false, current || 1084 1085 $.each(s.filters, function (attribute, filter) { 1086 var current = false; 1087 1088 if (attribute == "keyword") 1089 $.each(self.o.searchfields, function (i, field) { 1090 if (location[field] && !current) 1091 current = !( 1092 s 1093 .normalizeString(location[field]) 1094 .indexOf(s.normalizeString(filter)) == -1 1095 ); 1096 }); 1097 else if (location[attribute] && location[attribute].indexOf(filter) != -1) 1098 current = true; 1099 1100 matched = matched && current; 1101 }); 1102 1103 if (matched) { 1104 $('.mapplic-dir-item[data-location="' + location.id + '"]') 1105 .clone(true) 1106 .appendTo($(".mapplic-dir-results ul")); // results 1107 if (self.o.highlight && !$.isEmptyObject(s.filters) && location.el) 1108 location.el.addClass("mapplic-highlight"); // highlight 1109 } 1110 }); 1111 1112 s.sortby($(".mapplic-dir-results ul"), "title"); // alphabetic sort by title 1113 1114 if ($.isEmptyObject(s.filters)) { 1115 $(".mapplic-dir").show(); 1116 $(".mapplic-dir-results").hide(); 1117 } else { 1118 $(".mapplic-dir").hide(); 1119 $(".mapplic-dir-results").show(); 1120 if (self.o.highlight) self.map.addClass("mapplic-filtered"); 1121 } 1122 1123 $(".mapplic-dir-results h3 span").text( 1124 $(".mapplic-dir-results .mapplic-dir-item").length 1125 ); // count 1126 }; 1127 1128 this.clearFilters = function () { 1129 this.filters = {}; 1130 $(".mapplic-dir-search").val(""); 1131 $(".mapplic-dir-filter").val(""); 1132 1133 this.search(); 1134 }; 1135 1136 this.normalizeString = function (s) { 1137 if (s) 1138 return s 1139 .toLowerCase() 1140 .normalize("NFD") 1141 .replace(/[\u0300-\u036f]/g, ""); 1142 else return ""; 1143 }; 1144 1145 this.sort = function (dir, order) { 1146 if (order === undefined) order = 1; 1147 var items = dir.children(".mapplic-dir-item"); 1148 1149 items 1150 .sort(function (a, b) { 1151 var a = $(a).data("sort"), 1152 b = $(b).data("sort"); 1153 if (isNaN(a) || isNaN(b)) { 1154 return a < b ? -1 * order : 1 * order; 1155 } else return (a - b) * order; 1156 }) 1157 .appendTo(dir); 1158 }; 1159 1160 this.sortby = function (dir, attribute, order) { 1161 var s = this; 1162 1163 if (order === undefined) order = 1; 1164 var items = dir.children(".mapplic-dir-item"); 1165 items.each(function () { 1166 $(this).data( 1167 "sort", 1168 s.normalizeString(self.l[$(this).data("location")][attribute]) 1169 ); 1170 }); 1171 this.sort(dir, order); 1172 }; 1173 } 1174 1175 // sidebar 1176 function Sidebar() { 1177 this.el = null; 1178 this.header = null; 1179 this.input = null; 1180 this.list = null; 1181 this.tags = null; 1182 this.toggle = null; 1183 1184 this.init = function () { 1185 var s = this; 1186 1187 this.el = $("<div></div>").addClass("mapplic-sidebar").appendTo(self.el); 1188 1189 if (self.o.sidebartoggle) { 1190 self.el.addClass("mapplic-dynamic-sidebar"); 1191 1192 this.toggle = $("<button></button>") 1193 .append(getIcon("icon-sidebar")) 1194 .addClass("mapplic-button mapplic-sidebar-toggle") 1195 .appendTo(self.container.el); 1196 this.toggle.on("click touchstart", function (e) { 1197 e.preventDefault(); 1198 self.el.toggleClass("mapplic-hidden-sidebar"); 1199 setTimeout(function () { 1200 var wr = self.container.el.width() / self.contentWidth, 1201 hr = self.container.el.height() / self.contentHeight; 1202 1203 if (self.o.mapfill) { 1204 if (wr > hr) self.fitscale = wr; 1205 else self.fitscale = hr; 1206 } else { 1207 if (wr < hr) self.fitscale = wr; 1208 else self.fitscale = hr; 1209 } 1210 1211 if ( 1212 self.container.oldW != self.container.el.width() || 1213 self.container.oldH != self.container.el.height() 1214 ) { 1215 self.container.oldW = self.container.el.width(); 1216 self.container.oldH = self.container.el.height(); 1217 1218 if (self.scale * self.contentWidth < self.container.el.width()) 1219 self.moveTo(self.x, self.y, self.scale, 600); 1220 } 1221 }, 400); 1222 }); 1223 } 1224 1225 if (self.o.search) { 1226 this.header = $("<div></div>") 1227 .addClass("mapplic-sidebar-header") 1228 .append(getIcon("icon-magnifier")) 1229 .appendTo(this.el); 1230 this.headerwrap = $("<div></div>").appendTo(this.header); 1231 1232 this.input = $("<input>") 1233 .attr({ 1234 type: "text", 1235 spellcheck: "false", 1236 placeholder: self.o.searchPlaceHolder, 1237 }) 1238 .addClass("mapplic-search-input") 1239 .keyup(function (e) { 1240 var val = $(this).val(); 1241 1242 if (val) self.directory.filters["keyword"] = val; 1243 else delete self.directory.filters["keyword"]; 1244 1245 s.el.toggleClass("mapplic-sidebar-header-opened", val.length < 1); 1246 1247 s.search(); 1248 1249 if (e.keyCode == 13) 1250 $(".mapplic-list-container li > a").filter(":visible:first").click(); 1251 else if (e.keyCode == 27) $(this).blur(); 1252 }); 1253 this.input.focus(function () { 1254 if ($(this).val().length < 1) s.el.addClass("mapplic-sidebar-header-opened"); 1255 }); 1256 1257 $("body").click(function (e) { 1258 if (!$.contains(s.header[0], e.target) && s.header[0] != e.target) 1259 s.el.removeClass("mapplic-sidebar-header-opened"); 1260 }); 1261 1262 this.input.appendTo(this.headerwrap); 1263 1264 /*this.clear = $('<button></button>').append(getIcon('icon-cross')).addClass('mapplic-search-clear').attr('aria-label', self.loc.clearsearch).click(function() { 1265 s.el.toggleClass('mapplic-sidebar-header-opened'); 1266 //s.input.val(''); 1267 //s.search(); 1268 }).appendTo(this.headerwrap)*/ this.toggle = $("<button></button>") 1269 .append(getIcon("icon-filter")) 1270 .addClass("mapplic-search-toggle") 1271 .click(function (e) { 1272 e.preventDefault(); 1273 s.el.toggleClass("mapplic-sidebar-header-opened"); 1274 }) 1275 .appendTo(this.headerwrap); 1276 1277 // tags 1278 this.tags = $("<div></div>") 1279 .addClass("mapplic-filter-tags") 1280 .appendTo(this.headerwrap); 1281 1282 // filters 1283 this.filter = $("<div></div>").addClass("mapplic-filter").appendTo(this.header); 1284 1285 // dim 1286 this.dim = $("<div></div>") 1287 .addClass("mapplic-sidebar-dim") 1288 .click(function () { 1289 s.el.removeClass("mapplic-sidebar-header-opened"); 1290 }) 1291 .appendTo(this.el); 1292 } else this.el.addClass("mapplic-sidebar-nosearch"); 1293 1294 // items 1295 var event = "mapready"; 1296 if (self.o.csv) event = "csvready"; 1297 1298 self.el.on(event, function (e, self) { 1299 s.dir = self.directory 1300 .getDirectory() 1301 .addClass("mapplic-list-container") 1302 .appendTo(s.el); 1303 1304 // alphabetic sort by title 1305 if (self.o.alphabetic) self.directory.sortby(s.dir, "title"); 1306 }); 1307 1308 if (self.o.searchdescription && self.o.searchfields.indexOf("description") === -1) 1309 self.o.searchfields.push("description"); 1310 }; 1311 1312 this.addTag = function (item) { 1313 var s = this, 1314 categories = []; 1315 1316 if (self.directory.filters["category"]) 1317 categories = self.directory.filters["category"].split(","); 1318 1319 if (!categories.includes(item.id)) { 1320 categories.push(item.id); 1321 self.directory.filters["category"] = categories.toString(); 1322 1323 this.el.addClass("mapplic-sidebar-tagsrow"); 1324 1325 var tag = $("<button></button>") 1326 .addClass("mapplic-tag") 1327 .text(item.title) 1328 .attr("aria-label", self.loc.clearfilter + ": " + item.title) 1329 .prependTo(this.tags); 1330 $("<span style='color:black;'>X</span>").appendTo(tag); 1331 if (item.color) tag.css("background-color", item.color); 1332 if (item.id) tag.attr("data-id", item.id); 1333 1334 tag 1335 .click(function () { 1336 tag.remove(); 1337 if (s.tags.children().length < 1) 1338 s.el.removeClass("mapplic-sidebar-tagsrow"); 1339 if (self.directory.filters["category"] == item.id) 1340 delete self.directory.filters["category"]; 1341 else if (self.directory.filters["category"]) { 1342 categories = self.directory.filters["category"].split(","); 1343 var i = categories.indexOf(item.id); 1344 if (i > -1) { 1345 categories.splice(i, 1); 1346 self.directory.filters["category"] = categories.toString(); 1347 } 1348 } 1349 s.search(); 1350 }) 1351 .appendTo(tag); 1352 1353 s.search(); 1354 this.el.removeClass("mapplic-sidebar-header-opened"); 1355 } 1356 }; 1357 1358 this.addCategories = function (categories) { 1359 var s = this, 1360 list = $("<ul></ul>"), 1361 add = false;
1362 1363 if (categories) { 1364 $.each(categories, function (index, category) { 1365 category.nr = 0; 1366 1367 if (!(category.hide == "true")) { 1368 var item = $("<li></li>") 1369 .addClass("mapplic-list-category") 1370 .attr("data-category", category.id); 1371 var link = $("<a></a>").attr("href", "#").prependTo(item); 1372 1373 var thumbnail = self.directory 1374 .thumbnail(category.title, category.icon) 1375 .appendTo(link); 1376 if (category.color && thumbnail) 1377 thumbnail.css({ 1378 "background-color": category.color, 1379 "border-color": category.color, 1380 }); 1381 1382 var title = $("<h4></h4").text(category.title).appendTo(link); 1383 if (!category.about) title.addClass("mapplic-margin"); 1384 else 1385 $("<span></span>") 1386 .addClass("mapplic-about") 1387 .html(category.about) 1388 .appendTo(link); 1389 category.count = $("<span></span>") 1390 .text("(0)") 1391 .addClass("mapplic-list-count") 1392 .appendTo(title); 1393 1394 var toggle = self.legend.newToggle(category); 1395 if (toggle) toggle.appendTo(item); 1396 1397 link.on("click", function (e) { 1398 e.preventDefault(); 1399 if (category.nr < 1 && toggle) $("> input", toggle).trigger("click"); 1400 else { 1401 //s.input.val(''); 1402 s.addTag(category); 1403 } 1404 }); 1405 1406 category.list = item; 1407 item.appendTo(list); 1408 add = true; 1409 } 1410 }); 1411 1412 if (add) { 1413 s.el.addClass("mapplic-sidebar-filterable"); 1414 1415 //$('<h5></h5>').text('CATEGORIES').appendTo(s.filter); 1416 1417 list.appendTo(s.filter); 1418 } 1419 } 1420 }; 1421 1422 this.countCategory = function () { 1423 $.each(self.g, function (i, group) { 1424 if (group.count) { 1425 group.count.text("(" + group.nr + ")"); 1426 if (group.count[0]) { 1427 if (group.nr < 1) group.count[0].style.display = "none"; 1428 else group.count[0].style.display = ""; 1429 } 1430 } 1431 }); 1432 }; 1433 1434 // search 1435 this.search = function () { 1436 var s = this; 1437 1438 if (self.o.highlight) { 1439 self.map.removeClass("mapplic-filtered"); 1440 $(".mapplic-highlight", self.map).removeClass("mapplic-highlight"); 1441 } 1442 1443 $.each(self.l, function (i, location) { 1444 var matched = true; // matched false, current || 1445 1446 $.each(self.directory.filters, function (attribute, filter) { 1447 var current = false; 1448 1449 if (attribute == "keyword") 1450 $.each(self.o.searchfields, function (i, field) { 1451 if (location[field] && !current) 1452 current = !( 1453 self.directory 1454 .normalizeString(location[field]) 1455 .indexOf(self.directory.normalizeString(filter)) == -1 1456 ); 1457 }); 1458 else if (location.category && attribute == "category") { 1459 var categories = location.category; 1460 if (typeof categories == "string") categories = categories.split(","); 1461 1462 categories.forEach(function (category) { 1463 if (self.directory.filters["category"].split(",").includes(category)) 1464 current = true; 1465 }); 1466 } else if (location[attribute] && location[attribute].indexOf(filter) != -1) 1467 current = true; 1468 1469 matched = matched && current; 1470 }); 1471 1472 var item = $('.mapplic-dir-item[data-location="' + location.id + '"]', self.el); 1473 if (matched) { 1474 item.show(); 1475 if ( 1476 self.o.highlight && 1477 !$.isEmptyObject(self.directory.filters) && 1478 location.el 1479 ) 1480 location.el.addClass("mapplic-highlight"); // highlight 1481 } else item.hide(); 1482 }); 1483 1484 if (!$.isEmptyObject(self.directory.filters)) { 1485 if (self.o.highlight) self.map.addClass("mapplic-filtered"); 1486 } 1487 }; 1488 } 1489 1490 // developer tools 1491 function DevTools() { 1492 this.el = null; 1493 1494 this.init = function () { 1495 this.el = $("<div></div>") 1496 .addClass("mapplic-coordinates") 1497 .appendTo(self.container.el); 1498 this.el.append("x: "); 1499 $("<code></code>").addClass("mapplic-coordinates-x").appendTo(this.el); 1500 this.el.append(" y: "); 1501 $("<code></code>").addClass("mapplic-coordinates-y").appendTo(this.el); 1502 1503 $(".mapplic-layer", self.map).on("mousemove", function (e) { 1504 var x = (e.pageX - self.map.offset().left) / self.map.width() / self.scale, 1505 y = (e.pageY - self.map.offset().top) / self.map.height() / self.scale; 1506 $(".mapplic-coordinates-x").text(parseFloat(x).toFixed(4)); 1507 $(".mapplic-coordinates-y").text(parseFloat(y).toFixed(4)); 1508 }); 1509 1510 return this; 1511 }; 1512 } 1513 1514 // clear button 1515 function ClearButton() { 1516 this.el = null; 1517 1518 this.init = function () { 1519 this.el = $("<button></button>") 1520 .text(self.loc.resetzoom) 1521 .append(getIcon("icon-reset")) 1522 .addClass("mapplic-button mapplic-clear-button") 1523 .appendTo(self.container.el); 1524 1525 if (!self.o.zoombuttons) this.el.css("bottom", "0"); 1526 1527 this.el.on("click touchstart", function (e) { 1528 e.preventDefault(); 1529 self.hideLocation(); 1530 self.moveTo(0.5, 0.5, self.fitscale, 400); 1531 }); 1532 1533 return this; 1534 }; 1535 1536 this.update = function (scale) { 1537 if (scale == self.fitscale) this.el.stop().fadeOut(200); 1538 else this.el.stop().fadeIn(200); 1539 }; 1540 } 1541 1542 // zoom buttons 1543 function ZoomButtons() { 1544 this.el = null; 1545 1546 this.init = function () { 1547 this.el = $("<div></div>") 1548 .addClass("mapplic-zoom-buttons") 1549 .appendTo(self.container.el); 1550 1551 // zoom-in button 1552 this.zoomin = $("<button></button>") 1553 .attr("aria-label", self.loc.zoomin) 1554 .append(getIcon("icon-plus")) 1555 .addClass("mapplic-button mapplic-zoomin-button") 1556 .appendTo(this.el); 1557 this.zoomin.on("click touchstart", function (e) { 1558 e.preventDefault(); 1559 self.container.stopMomentum(); 1560 1561 var scale = self.scale; 1562 self.scale = normalizeScale(scale + scale * 0.8); 1563 1564 self.x = normalizeX( 1565 self.x - (self.container.el.width() / 2 - self.x) * (self.scale / scale - 1) 1566 ); 1567 self.y = normalizeY( 1568 self.y - (self.container.el.height() / 2 - self.y) * (self.scale / scale - 1) 1569 ); 1570 1571 zoomTo(self.x, self.y, self.scale, 400, "ease"); 1572 }); 1573 1574 // zoom-out button 1575 this.zoomout = $("<button></button>") 1576 .attr("aria-label", self.loc.zoomout) 1577 .append(getIcon("icon-minus")) 1578 .addClass("mapplic-button mapplic-zoomout-button") 1579 .appendTo(this.el); 1580 this.zoomout.on("click touchstart", function (e) { 1581 e.preventDefault(); 1582 self.container.stopMomentum(); 1583 1584 var scale = self.scale; 1585 self.scale = normalizeScale(scale - scale * 0.4); 1586 1587 self.x = normalizeX( 1588 self.x - (self.container.el.width() / 2 - self.x) * (self.scale / scale - 1) 1589 ); 1590 self.y = normalizeY( 1591 self.y - (self.container.el.height() / 2 - self.y) * (self.scale / scale - 1) 1592 ); 1593 1594 zoomTo(self.x, self.y, self.scale, 400, "ease"); 1595 }); 1596 1597 return this; 1598 }; 1599 1600 this.update = function (scale) { 1601 this.zoomin.removeAttr("disabled"); 1602 this.zoomout.removeAttr("disabled"); 1603 if (scale == self.fitscale) this.zoomout.attr("disabled", "disabled"); 1604 else if (scale == 1) this.zoomin.attr("disabled", "disabled"); 1605 }; 1606 } 1607 1608 // fullscreen 1609 function Fullscreen() { 1610 this.el = null; 1611 this.fsh = null; // fullscreen placeholder 1612 this.esh = null; // element placeholder 1613 1614 this.init = function () { 1615 var s = this; 1616 1617 this.fph = $("<div></div>").addClass("mapplic-fsh").prependTo("body"); 1618 this.eph = $("<div></div>").addClass("mapplic-esh").insertBefore(self.el); 1619 1620 // fullscreen button 1621 this.el = $("<button></button>") 1622 .append(getIcon("icon-fullscreen")) 1623 .append(getIcon("icon-fullscreen-exit")) 1624 .addClass("mapplic-button mapplic-fullscreen-button") 1625 .click(function (e) { 1626 self.el.toggleClass("mapplic-fullscreen"); 1627 1628 if (self.el.hasClass("mapplic-fullscreen")) self.el.insertBefore(s.fph); 1629 else self.el.insertBefore(s.eph); 1630 1631 $(document).resize(); 1632 }) 1633 .appendTo(self.container.el); 1634 1635 if (self.o.wayfinder) { 1636 // wayfinder always in fullscreen mode 1637 this.el.click(); 1638 } else { 1639 // esc key 1640 $(document).on("keyup.mapplic", function (e) { 1641 if (e.keyCode === 27 && $(".mapplic-fullscreen")[0]) { 1642 $(".mapplic-element.mapplic-fullscreen").removeClass("mapplic-fullscreen"); 1643 self.el.insertBefore(s.eph); 1644 $(document).resize(); 1645 } 1646 }); 1647 } 1648 }; 1649 } 1650 1651 // styles 1652 function Styles() { 1653 this.data = null; 1654 1655 this.init = function (data) { 1656 this.data = data; 1657 this.process(data); 1658 return this; 1659 }; 1660 1661 this.process = function (styles) { 1662 var css = ""; 1663 1664 // basecolor 1665 if (self.o.basecolor) 1666 css += this.rule( 1667 ".mapplic-fullscreen, .mapplic-legend", 1668 "background-color", 1669 self.o.basecolor 1670 ); 1671 1672 // bgcolor 1673 if (self.o.bgcolor) { 1674 css += this.rule( 1675 ".mapplic-button, .mapplic-tooltip-close .mapplic-icon, .mapplic-levels-select, .mapplic-levels button, .mapplic-level-switcher button.mapplic-selected, .mapplic-element *::-webkit-scrollbar-track, .mapplic-list-container, .mapplic-filter, .mapplic-sidebar-header, .mapplic-tooltip-wrap, .mapplic-lightbox, .mapplic-toggle:before", 1676 "background-color", 1677 self.o.bgcolor 1678 ); 1679 css += this.rule( 1680 ".mapplic-legend-key, .mapplic-element *::-webkit-scrollbar-thumb", 1681 "border-color", 1682 self.o.bgcolor 1683 ); 1684 css += this.rule( 1685 ".mapplic-tooltip:after", 1686 "border-color", 1687 self.o.bgcolor + " transparent transparent transparent !important" 1688 ); 1689 css += this.rule( 1690 ".mapplic-tooltip-bottom.mapplic-tooltip:after", 1691 "border-color", 1692 "transparent transparent " + self.o.bgcolor + " transparent !important" 1693 ); 1694 } 1695 1696 // bgcolor2 1697 if (self.o.bgcolor2) { 1698 css += this.rule( 1699 ".mapplic-thumbnail-placeholder, .mapplic-level-switcher button, .mapplic-sidebar .mapplic-dir-item > a:hover, .mapplic-sidebar-header-opened.mapplic-sidebar-filterable .mapplic-search-toggle, .mapplic-sidebar .mapplic-dir-item > a:focus, .mapplic-sidebar .mapplic-dir-item.mapplic-active > a, .mapplic-list-category > a:hover, .mapplic-zoom-buttons button:disabled, .mapplic-levels button:disabled", 1700 "background-color", 1701 self.o.bgcolor2 1702 ); 1703 css += this.rule("a.mapplic-zoomin-button", "border-color", self.o.bgcolor2); 1704 } 1705 1706 // headingcolor 1707 if (self.o.headingcolor) { 1708 css += this.rule( 1709 ".mapplic-search-input, .mapplic-level-switcher button.mapplic-selected, .mapplic-list-category > a, .mapplic-tooltip-title, .mapplic-lightbox-title, .mapplic-sidebar .mapplic-dir-item h4, .mapplic-element strong, .mapplic-levels-select, .mapplic-list-category h4", 1710 "color", 1711 self.o.headingcolor 1712 ); 1713 css += this.rule(".mapplic-icon", "fill", self.o.headingcolor); 1714 } 1715 1716 // textcolor 1717 if (self.o.textcolor) 1718 css += this.rule( 1719 ".mapplic-element, .mapplic-element a, .mapplic-level-switcher button, .mapplic-about, .mapplic-list-category > a .mapplic-list-count, .mapplic-search-input::placeholder, .mapplic-lightbox-description", 1720 "color", 1721 self.o.textcolor 1722 ); 1723 1724 // accentcolor 1725 if (self.o.accentcolor) 1726 css += this.rule( 1727 ".mapplic-popup-link, .mapplic-accentcolor", 1728 "background-color", 1729 self.o.accentcolor 1730 ); 1731 1732 if (styles) { 1733 self.s = []; 1734
1735 styles.forEach(function (s) { 1736 // styles object 1737 self.s[s.class] = s; 1738 1739 if (s.base) { 1740 css += 1741 "." + 1742 s.class + 1743 ".mapplic-clickable:not(g), g." + 1744 s.class + 1745 ".mapplic-clickable > * { "; 1746 $.each(s.base, function (prop, val) { 1747 css += prop + ": " + val + "; "; 1748 }); 1749 css += "}\n"; 1750 1751 css += "." + s.class + ".mapplic-pin {\n"; 1752 $.each(s.base, function (prop, val) { 1753 css += " background-color: " + val + ";\n"; 1754 css += " border-color: " + val + ";\n"; 1755 }); 1756 css += "}\n\n"; 1757 } 1758 1759 if (s.hover) { 1760 css += 1761 "." + 1762 s.class + 1763 ".mapplic-highlight:not(g), g." + 1764 s.class + 1765 ".mapplic-highlight > *, ." + 1766 s.class + 1767 ".mapplic-clickable:not(g):hover, g." + 1768 s.class + 1769 ".mapplic-clickable:hover > * { "; 1770 $.each(s.hover, function (prop, val) { 1771 css += prop + ": " + val + "; "; 1772 }); 1773 css += "}\n"; 1774 1775 css += 1776 "." + 1777 s.class + 1778 ".mapplic-pin.mapplic-highlight, ." + 1779 s.class + 1780 ".mapplic-pin:hover {\n"; 1781 $.each(s.hover, function (prop, val) { 1782 css += " background-color: " + val + ";\n"; 1783 css += " border-color: " + val + ";\n"; 1784 }); 1785 css += "}\n\n"; 1786 } 1787 1788 if (s.active) { 1789 css += 1790 "." + 1791 s.class + 1792 ".mapplic-active:not(g), g." + 1793 s.class + 1794 ".mapplic-active > * { "; 1795 $.each(s.active, function (prop, val) { 1796 css += prop + ": " + val + " !important; "; 1797 }); 1798 css += "}\n"; 1799 1800 css += "." + s.class + ".mapplic-pin.mapplic-active {\n"; 1801 $.each(s.active, function (prop, val) { 1802 css += " background-color: " + val + ";\n"; 1803 css += " border-color: " + val + ";\n"; 1804 }); 1805 css += "}\n\n"; 1806 } 1807 }); 1808 } 1809 1810 if (self.o.customcss) css += self.o.customcss; 1811 1812 var style = $("<style></style>").html(css).appendTo("body"); 1813 }; 1814 1815 this.rule = function (selector, attribute, value) { 1816 var css = selector + " {\n"; 1817 css += " " + attribute + ": " + value + ";\n"; 1818 css += "}\n\n"; 1819 1820 return css; 1821 }; 1822 } 1823 1824 function Container() { 1825 this.el = null; 1826 this.oldW = 0; 1827 this.oldH = 0; 1828 (this.position = { x: 0, y: 0 }), (this.momentum = null); 1829 this.levels = {}; 1830 1831 this.init = function () { 1832 this.el = $("<div></div>").addClass("mapplic-container").appendTo(self.el); 1833 self.map = $("<div></div>").addClass("mapplic-map").appendTo(this.el); 1834 1835 self.map.css({ 1836 width: self.contentWidth, 1837 height: self.contentHeight, 1838 }); 1839 1840 return this; 1841 }; 1842 1843 // returns container height (px) 1844 this.calcHeight = function (v) { 1845 var val = Math.min( 1846 Math.max(this.getHeight(v), this.getHeight(self.o.minheight)), 1847 this.getHeight(self.o.maxheight) 1848 ); 1849 1850 if ($.isNumeric(val)) return val; 1851 else return false; 1852 }; 1853 1854 this.getHeight = function (v) { 1855 var val = v.toString().replace("px", ""); 1856 1857 if (val == "auto" && self.container.el) { 1858 val = (self.container.el.width() * self.contentHeight) / self.contentWidth; 1859 } else if (val.slice(-1) == "%") { 1860 val = ($(window).height() * val.replace("%", "")) / 100; 1861 } 1862 1863 return val; 1864 }; 1865 1866 this.resetZoom = function () { 1867 var init = self.l["init"]; 1868 if (init) { 1869 self.switchLevel(init.level); 1870 if (!self.bboxZoom(init.el)) self.zoomLocation(init); 1871 } else self.moveTo(0.5, 0.5, self.fitscale, 0); 1872 }; 1873 1874 this.revealChild = function (parent) { 1875 $(".mapplic-pin[data-location^=" + parent.id + "-]", self.map).addClass( 1876 "mapplic-revealed" 1877 ); 1878 $(".mapplic-map-image [id^=" + parent.id + "-]", self.map).addClass( 1879 "mapplic-revealed" 1880 ); 1881 }; 1882 1883 this.revealZoom = function (zoom) { 1884 $(".mapplic-pin[data-reveal]", self.map).each(function () { 1885 var reveal = $(this).data("reveal"); 1886 if (zoom * self.o.maxscale >= reveal) $(this).addClass("mapplic-revealed"); 1887 else $(this).removeClass("mapplic-revealed"); 1888 }); 1889 }; 1890 1891 this.coverAll = function () { 1892 $(".mapplic-revealed", self.map).removeClass("mapplic-revealed"); 1893 }; 1894 1895 this.stopMomentum = function () { 1896 cancelAnimationFrame(this.momentum); 1897 if (this.momentum != null) { 1898 self.x = this.position.x; 1899 self.y = this.position.y; 1900 } 1901 this.momentum = null; 1902 }; 1903 1904 this.addLevelSwitcher = function () { 1905 if (self.data.levels.length > 1) { 1906 var control = $("<div></div>").addClass("mapplic-level-switcher");
1907 self.data.levels.forEach(function (level, i) { 1908 var button = $("<button></button>") 1909 .attr("data-level", level.id) 1910 .text(level.title) 1911 .prependTo(control) 1912 .click(function (e) { 1913 e.preventDefault(); 1914 self.switchLevel(level.id); 1915 }); 1916 if (level.show) button.addClass("mapplic-selected"); 1917 }); 1918 1919 this.el.append(control); 1920 1921 self.el.on("levelswitched", function (e, target) { 1922 $("button", control).removeClass("mapplic-selected"); 1923 $('button[data-level="' + target + '"]', control).addClass( 1924 "mapplic-selected" 1925 ); 1926 }); 1927 } 1928 }; 1929 1930 this.addControls = function () { 1931 self.map.addClass("mapplic-zoomable"); 1932 1933 document.ondragstart = function () { 1934 return false; 1935 }; // IE drag fix 1936 1937 // momentum 1938 var friction = 0.85, 1939 mouse = { x: 0, y: 0 }, 1940 pre = { x: 0, y: 0 }, 1941 previous = { x: this.position.x, y: this.position.y }, 1942 velocity = { x: 0, y: 0 }; 1943 1944 var s = this; 1945 var momentumStep = function () { 1946 s.momentum = requestAnimationFrame(momentumStep); 1947 1948 if (self.map.hasClass("mapplic-dragging")) { 1949 pre.x = previous.x; 1950 pre.y = previous.y; 1951 1952 previous.x = s.position.x; 1953 previous.y = s.position.y; 1954 1955 s.position.x = mouse.x; 1956 s.position.y = mouse.y; 1957 1958 velocity.x = previous.x - pre.x; 1959 velocity.y = previous.y - pre.y; 1960 } else { 1961 s.position.x += velocity.x; 1962 s.position.y += velocity.y; 1963 1964 velocity.x *= friction; 1965 velocity.y *= friction; 1966 1967 if (Math.abs(velocity.x) + Math.abs(velocity.y) < 0.1) { 1968 s.stopMomentum(); 1969 self.x = s.position.x; 1970 self.y = s.position.y; 1971 } 1972 } 1973 s.position.x = normalizeX(s.position.x); 1974 s.position.y = normalizeY(s.position.y); 1975 1976 zoomTo(s.position.x, s.position.y); 1977 }; 1978 1979 // drag & drop 1980 $(".mapplic-map-image", self.map).on("mousedown", function (e) { 1981 self.dragging = false; 1982 self.map.addClass("mapplic-dragging"); 1983 1984 s.stopMomentum(); 1985 var initial = { 1986 x: e.pageX - self.x, 1987 y: e.pageY - self.y, 1988 }; 1989 1990 mouse.x = normalizeX(e.pageX - initial.x); 1991 mouse.y = normalizeY(e.pageY - initial.y); 1992 momentumStep(); 1993 1994 self.map.on("mousemove", function (e) { 1995 self.dragging = true; 1996 1997 mouse.x = normalizeX(e.pageX - initial.x); 1998 mouse.y = normalizeY(e.pageY - initial.y); 1999 }); 2000 2001 $(document).on("mouseup.mapplic", function () { 2002 $(document).off("mouseup.mapplic"); 2003 self.map.off("mousemove"); 2004 self.map.removeClass("mapplic-dragging"); 2005 }); 2006 }); 2007 2008 // mousewheel 2009 if (self.o.mousewheel) 2010 $(".mapplic-map-image", self.el).on( 2011 "mousewheel DOMMouseScroll", 2012 self.mouseWheel 2013 ); 2014 2015 // touch 2016 var init1 = null, 2017 init2 = null, 2018 initD = 0, 2019 initScale = null; 2020 2021 $(".mapplic-map-image", self.map).on("touchstart", function (e) { 2022 e.preventDefault(); 2023 var eo = e.originalEvent; 2024 2025 if (eo.touches.length == 1) { 2026 self.map.addClass("mapplic-dragging"); 2027 self.dragging = false; 2028 2029 s.stopMomentum(); 2030 2031 init1 = { 2032 x: eo.touches[0].pageX - self.x, 2033 y: eo.touches[0].pageY - self.y, 2034 }; 2035 2036 self.firstcoord = { 2037 x: eo.touches[0].pageX, 2038 y: eo.touches[0].pageY, 2039 }; 2040 2041 mouse = { 2042 x: normalizeX(eo.touches[0].pageX - init1.x), 2043 y: normalizeY(eo.touches[0].pageY - init1.y), 2044 }; 2045 momentumStep(); 2046 2047 self.map.on("touchmove", function (e) { 2048 e.preventDefault(); 2049 self.dragging = true; 2050 var eo = e.originalEvent; 2051 2052 if (eo.touches.length == 1) { 2053 mouse.x = normalizeX(eo.touches[0].pageX - init1.x); 2054 mouse.y = normalizeY(eo.touches[0].pageY - init1.y); 2055 2056 self.lastcoord = { 2057 x: eo.touches[0].pageX, 2058 y: eo.touches[0].pageY, 2059 }; 2060 } else if (eo.touches.length > 1) { 2061 var pos = { 2062 x: (eo.touches[0].pageX + eo.touches[1].pageX) / 2, 2063 y: (eo.touches[0].pageY + eo.touches[1].pageY) / 2, 2064 }; 2065 2066 var dist = 2067 Math.sqrt( 2068 Math.pow(eo.touches[0].pageX - eo.touches[1].pageX, 2) + 2069 Math.pow(eo.touches[0].pageY - eo.touches[1].pageY, 2) 2070 ) / initD; 2071 2072 var scale = self.scale; 2073 self.scale = normalizeScale(initScale * dist); 2074 2075 self.x = normalizeX( 2076 self.x - 2077 (pos.x - self.container.el.offset().left - self.x) * 2078 (self.scale / scale - 1) 2079 ); 2080 self.y = normalizeY( 2081 self.y - 2082 (pos.y - self.container.el.offset().top - self.y) * 2083 (self.scale / scale - 1) 2084 ); 2085 2086 zoomTo(self.x, self.y, self.scale, 100, "ease"); 2087 } 2088 }); 2089 2090 $(document).on("touchend.mapplic", function (e) { 2091 e.preventDefault(); 2092 var dragback = null, 2093 eo = e.originalEvent; 2094 2095 if (eo.touches.length == 0) { 2096 clearTimeout(dragback); 2097 $(document).off("touchend.mapplic"); 2098 self.map.off("touchmove"); 2099 self.map.removeClass("mapplic-dragging"); 2100 self.dragging = false;
2101 } else if (eo.touches.length == 1) { 2102 dragback = setTimeout(function () { 2103 self.map.addClass("mapplic-dragging"); 2104 self.dragging = true; 2105 2106 s.stopMomentum(); 2107 init1 = { 2108 x: eo.touches[0].pageX - self.x, 2109 y: eo.touches[0].pageY - self.y, 2110 }; 2111 2112 mouse = { 2113 x: normalizeX(eo.touches[0].pageX - init1.x), 2114 y: normalizeY(eo.touches[0].pageY - init1.y), 2115 }; 2116 2117 momentumStep(); 2118 }, 60); 2119 } 2120 }); 2121 } 2122 2123 // pinch 2124 else if (eo.touches.length == 2) { 2125 self.dragging = true; 2126 self.map.addClass("mapplic-dragging"); 2127 2128 s.stopMomentum(); 2129 2130 init2 = { 2131 x: eo.touches[1].pageX - self.x, 2132 y: eo.touches[1].pageY - self.y, 2133 }; 2134 initD = Math.sqrt( 2135 Math.pow(init1.x - init2.x, 2) + Math.pow(init1.y - init2.y, 2) 2136 ); 2137 initScale = self.scale; 2138 } 2139 }); 2140 }; 2141 } 2142 2143 // functions 2144 var processData = function (data) { 2145 self.data = data; 2146 self.g = {}; 2147 self.l = {}; 2148 var shownLevel = null; 2149 2150 // extend options 2151 self.o = $.extend(self.o, data); 2152 $.each(self.el.data(), function (i, v) { 2153 self.o[i] = v; 2154 }); 2155 self.o.zoommargin = parseFloat(self.o.zoommargin); 2156 self.o.maxscale = parseFloat(self.o.maxscale); 2157 2158 // more text 2159 if (self.o.moretext) self.loc.more = self.o.moretext; 2160 2161 // height of container 2162 if (self.el.data("height")) self.o.height = self.el.data("height"); 2163 self.contentWidth = parseFloat(data.mapwidth); 2164 self.contentHeight = parseFloat(data.mapheight); 2165 2166 // limiting to scale 1 2167 self.contentWidth = self.contentWidth * self.o.maxscale; 2168 self.contentHeight = self.contentHeight * self.o.maxscale; 2169 2170 // create container 2171 self.container = new Container().init(); 2172 2173 // styles 2174 self.styles = new Styles().init(self.o.styles); 2175 2176 // create minimap 2177 if (self.o.minimap) self.minimap = new Minimap().init(); 2178 2179 // create legend 2180 self.legend = new Legend().init(); 2181 self.legend.build(data.groups || data.categories); 2182 2183 // directory 2184 self.directory = new Directory().init(); 2185 2186 // create sidebar 2187 if (self.o.sidebar) { 2188 self.sidebar = new Sidebar(); 2189 self.sidebar.init(); 2190 } else self.container.el.css("width", "100%"); 2191 2192 // groups 2193 self.groups = new Groups(); 2194 self.groups.addGroups(data.groups || data.categories); 2195 2196 // trigger event 2197 self.el.trigger("mapstart", self); 2198 2199 // iterate through levels 2200 var levelnr = 0, 2201 toload = 0; 2202 2203 if (data.levels) { 2204 $.each(data.levels, function (index, level) { 2205 var source = level.map, 2206 extension = source.substr(source.lastIndexOf(".") + 1).toLowerCase(); 2207 2208 // new map layer 2209 var layer = $("<div></div>") 2210 .addClass("mapplic-layer") 2211 .attr("data-floor", level.id) 2212 .appendTo(self.map); 2213 switch (extension) { 2214 // image formats 2215 case "jpg": 2216 case "jpeg": 2217 case "png": 2218 case "gif": 2219 $("<img>") 2220 .attr("src", source) 2221 .attr("aria-hidden", "true") 2222 .addClass("mapplic-map-image") 2223 .appendTo(layer); 2224 if (level.locations) self.addLocations(level.locations, level.id); 2225 break; 2226 2227 // vector format 2228 case "svg": 2229 toload++; 2230 var mapimage = $("<div></div>") 2231 .addClass("mapplic-map-image") 2232 .appendTo(layer); 2233 2234 $("<div></div>").load(source, function () { 2235 // sanitize svg - XSS protection 2236 $("script", this).remove(); 2237 mapimage.html($(this).html()); 2238 2239 // illustrator duplicate id fix 2240 $(self.o.selector, mapimage).each(function () { 2241 var id = $(this).attr("id"); 2242 if (id) $(this).attr("id", id.replace(/_[1-9]+_$/g, "")); 2243 }); 2244 2245 // add locations 2246 if (level.locations) self.addLocations(level.locations, level.id); 2247 2248 // click event 2249 $(self.o.selector, mapimage).on("click touchend", function (e) { 2250 // display polygon id as hash 2251 window.location.hash = "roomId=" + ($(this).attr("id") || ""); 2252 2253 // display coords info inside console 2254 const pos = adjustedPosition([e.currentTarget]); 2255 console.info(`x: ${pos?.x?.toFixed(4)}, y: ${pos?.y?.toFixed(4)}`); 2256 2257 var shift = 2258 Math.abs(self.firstcoord.x - self.lastcoord.x) + 2259 Math.abs(self.firstcoord.y - self.lastcoord.y); 2260 if (!self.dragging || shift < 4) 2261 self.showLocation($(this).attr("id"), 600); 2262 }); 2263 2264 // autopopulate 2265 if (self.o.autopopulate) { 2266 var ap = []; 2267 $(self.o.selector, mapimage).each(function () { 2268 var id = $(this).attr("id"), 2269 location = self.l[id]; 2270 2271 if (!location) { 2272 location = { 2273 id: id, 2274 title: id.charAt(0).toUpperCase() + id.slice(1), 2275 pin: "hidden", 2276 }; 2277 ap.push(location); 2278 } 2279 }); 2280 self.addLocations(ap, level.id); 2281 } 2282 2283 // trigger event(s) 2284 self.el.trigger("svgloaded", [mapimage, level.id]); 2285 toload--; 2286 if (toload == 0) mapReady(); 2287 }); 2288 break; 2289 2290 // others 2291 default: 2292 // alert("File type " + extension + " is not supported!"); 2293 } 2294 2295 // create new minimap layer 2296 if (self.minimap) self.minimap.addLayer(level); 2297 2298 // shown level 2299 if (!shownLevel || level.show) shownLevel = level.id; 2300 2301 levelnr++; 2302 }); 2303 } 2304 2305 // COMPONENTS 2306 self.tooltips = []; 2307 if (self.o.lightbox && $.magnificPopup) self.lightbox = new Lightbox().init(); 2308 if (self.o.hovertip) self.hovertip = new HoverTooltip().init(); 2309 if (self.o.external) self.external = new External().init(); 2310 if (self.o.clearbutton) self.clearbutton = new ClearButton().init(); 2311 if (self.o.zoombuttons) self.zoombuttons = new ZoomButtons().init(); 2312 if (self.o.fullscreen) self.fullscreen = new Fullscreen().init(); 2313 if (self.o.developer) self.devtools = new DevTools().init(); 2314 2315 self.container.addLevelSwitcher(); 2316 self.switchLevel(shownLevel); 2317 2318 // enable wayfinder 2319 if (self.o.wayfinder) self.wayfinder = new Wayfinder().init(); 2320 2321 if (self.o.portrait === "true") self.o.portrait = true; 2322 2323 // resize 2324 $(window) 2325 .resize(function () { 2326 if ( 2327 self.o.portrait == true || 2328 ($.isNumeric(self.o.portrait) && 2329 self.el.width() < parseFloat(self.o.portrait)) 2330 ) { 2331 self.el.addClass("mapplic-portrait"); 2332 if (self.el.hasClass("mapplic-fullscreen")) 2333 self.container.el.height($(window).height()); 2334 else self.container.el.height(self.container.calcHeight(self.o.height)); 2335 } else { 2336 self.el.removeClass("mapplic-portrait"); 2337 self.container.el.height("100%"); 2338 self.el.height(self.container.calcHeight(self.o.height)); 2339 } 2340 2341 var wr = self.container.el.width() / self.contentWidth, 2342 hr = self.container.el.height() / self.contentHeight; 2343 2344 if (self.o.mapfill) { 2345 if (wr > hr) self.fitscale = wr; 2346 else self.fitscale = hr; 2347 } else { 2348 if (wr < hr) self.fitscale = wr; 2349 else self.fitscale = hr; 2350 } 2351 2352 if ( 2353 self.container.oldW != self.container.el.width() || 2354 self.container.oldH != self.container.el.height() 2355 ) { 2356 self.container.oldW = self.container.el.width(); 2357 self.container.oldH = self.container.el.height(); 2358 2359 self.container.resetZoom(); 2360 } 2361 2362 // adjust map legend position 2363 if (self.el.hasClass("mapplic-portrait")) { 2364 const legendOverflow = 160; 2365 const legendTop = self.container.el.height() - legendOverflow; 2366 self.legend.el.css("top", `${legendTop}px`); 2367 2368 self.legend.el.css("bottom", ""); 2369 self.el.css("margin-bottom", "0"); 2370 2371 const adjustedY = self.legend.el.height() - legendOverflow; 2372 if (adjustedY > 0) {
2373 self.container.el.css("margin-bottom", `${adjustedY}px`); 2374 } 2375 } else { 2376 self.legend.el.css("top", ""); 2377 self.container.el.css("margin-bottom", "0"); 2378 2379 const adjustedY = self.legend.el.height() + 20; 2380 self.legend.el.css("bottom", `-${adjustedY}px`); 2381 self.el.css("margin-bottom", `${adjustedY}px`); 2382 } 2383 }) 2384 .resize(); 2385 2386 // deeplinking 2387 if (self.o.deeplinking) { 2388 if (history.pushState && typeof URL == "function") 2389 self.deeplinking = new Deeplinking(); 2390 else self.deeplinking = new DeeplinkingHash(); 2391 2392 self.deeplinking.init(); 2393 } 2394 2395 // trigger event 2396 if (toload == 0) mapReady(); 2397 2398 // controls 2399 if (self.o.zoom) self.container.addControls(); 2400 self.firstcoord = self.lastcoord = {}; 2401 2402 // link to locations 2403 $(document).on("click", ".mapplic-location", function (e) { 2404 e.preventDefault(); 2405 self.showLocation($(this).attr("href").substr(1), 400); 2406 $("html, body").animate({ scrollTop: self.container.el.offset().top }, 400); 2407 }); 2408 }; 2409 2410 var mapReady = function () { 2411 // separate location array 2412 self.addLocations(self.data.locations); 2413 2414 // trigger event 2415 self.el.trigger("mapready", self); 2416 2417 // apply toggle 2418 self.legend.applyToggles(); 2419 2420 // CSV support 2421 if (self.o.csv) { 2422 Papa.parse(self.o.csv, { 2423 header: true, 2424 download: true, 2425 encoding: "UTF-8", 2426 skipEmptyLines: true, 2427 complete: function (results, file) { 2428 self.addLocations(results.data); 2429 $(".mapplic-pin", self.map).css({ 2430 transform: "scale(" + 1 / self.scale + ")", 2431 }); 2432 if (self.deeplinking) self.deeplinking.check(0); 2433 2434 self.el.trigger("csvready", self); 2435 }, 2436 }); 2437 } 2438 2439 self.container.resetZoom(); 2440 if (self.deeplinking) self.deeplinking.check(0); 2441 2442 // landmark mode 2443 if (self.el.data("landmark")) self.o.landmark = self.el.data("landmark"); 2444 if (self.o.landmark) { 2445 // Custom settings 2446 self.o.sidebar = false; 2447 self.o.zoombuttons = false; 2448 self.o.deeplinking = false; 2449 self.showLocation(self.o.landmark, 0, true); 2450 } 2451 2452 // wayfinder pins 2453 if (self.wayfinder) self.wayfinder.update(); 2454 }; 2455 2456 /* PRIVATE METHODS */ 2457 2458 // Web Mercator (EPSG:3857) lat/lng projection 2459 var latlngToPos = function (lat, lng) { 2460 var deltaLng = self.data.rightLng - self.data.leftLng, 2461 bottomLatDegree = (self.data.bottomLat * Math.PI) / 180, 2462 mapWidth = ((self.data.mapwidth / deltaLng) * 360) / (2 * Math.PI), 2463 mapOffsetY = 2464 (mapWidth / 2) * 2465 Math.log((1 + Math.sin(bottomLatDegree)) / (1 - Math.sin(bottomLatDegree))); 2466 2467 lat = (lat * Math.PI) / 180; 2468 2469 return { 2470 x: 2471 ((lng - self.data.leftLng) * (self.data.mapwidth / deltaLng)) / 2472 self.data.mapwidth, 2473 y: 2474 (self.data.mapheight - 2475 ((mapWidth / 2) * Math.log((1 + Math.sin(lat)) / (1 - Math.sin(lat))) - 2476 mapOffsetY)) / 2477 self.data.mapheight, 2478 }; 2479 }; 2480 2481 var adjustedPosition = function (element) { 2482 if (!element || !(element[0] instanceof SVGElement)) return false; 2483 2484 var viewBox = element[0].closest("svg")?.viewBox?.baseVal; 2485 var bbox = element[0]?.getBBox(); 2486 if (!viewBox || !bbox) return false; 2487 2488 return { 2489 x: (bbox.x + bbox.width / 2) / viewBox.width, 2490 y: (bbox.y + bbox.height / 2) / viewBox.height, 2491 }; 2492 }; 2493 2494 var estimatedPosition = function (element) { 2495 if (!element || !(element[0] instanceof SVGElement)) return false; 2496 2497 var bbox = element[0].getBBox(); 2498 2499 var padding = 40, 2500 wr = self.container.el.width() / (bbox.width + padding), 2501 hr = self.container.el.height() / (bbox.height + padding); 2502 2503 return { 2504 x: ((bbox.x + bbox.width / 2) / self.contentWidth) * self.o.maxscale, 2505 y: ((bbox.y + bbox.height / 2) / self.contentHeight) * self.o.maxscale, 2506 scale: Math.min(wr, hr) / self.o.maxscale, 2507 }; 2508 }; 2509 2510 var getIcon = function (name) { 2511 return ( 2512 '<svg class="mapplic-icon mapplic-' + 2513 name + 2514 '" aria-hidden="true"><use xlink:href="' + 2515 self.loc.iconfile + 2516 "#" + 2517 name + 2518 '"></use></svg>' 2519 ); 2520 }; 2521 2522 // normalizing x, y and scale 2523 var normalizeX = function (x) { 2524 var minX = (self.container.el.width() - self.contentWidth * self.scale).toFixed(4); 2525 if (minX < 0) { 2526 if (x > self.o.zoommargin) x = self.o.zoommargin; 2527 else if (x < minX - self.o.zoommargin) x = minX - self.o.zoommargin; 2528 } else x = minX / 2; 2529 2530 return x; 2531 }; 2532 2533 var normalizeY = function (y) { 2534 var minY = (self.container.el.height() - self.contentHeight * self.scale).toFixed( 2535 4 2536 ); 2537 if (minY < 0) { 2538 if (y > self.o.zoommargin) y = self.o.zoommargin; 2539 else if (y < minY - self.o.zoommargin) y = minY - self.o.zoommargin; 2540 } else y = minY / 2; 2541 2542 return y; 2543 }; 2544 2545 var normalizeScale = function (scale) { 2546 if (self.fitscale > 1) return self.fitscale; // no zoom 2547 2548 if (scale <= self.fitscale) scale = self.fitscale; 2549 else if (scale > 1) scale = 1; 2550 2551 // zoom timeout 2552 clearTimeout(self.zoomTimeout); 2553 self.zoomTimeout = setTimeout(function () { 2554 if (self.zoombuttons) self.zoombuttons.update(scale); 2555 if (self.clearbutton) self.clearbutton.update(scale); 2556 if (scale == self.fitscale) { 2557 self.container.coverAll(); 2558 if (self.o.closezoomout) self.hideLocation(); 2559 } 2560 self.container.revealZoom(scale); 2561 }, 200); 2562 2563 return scale; 2564 }; 2565 2566 var moveTimeout = null; 2567 2568 var zoomTo = function (x, y, scale, d) { 2569 d = typeof d !== "undefined" ? d / 1000 : 0; 2570 2571 // move class 2572 self.el.addClass("mapplic-move"); 2573 clearTimeout(moveTimeout); 2574 moveTimeout = setTimeout(function () { 2575 self.el.removeClass("mapplic-move"); 2576 self.el.trigger("positionchanged", location); 2577 }, 400); 2578 2579 // transforms 2580 self.map.css({ 2581 transition: "transform " + d + "s", 2582 transform: 2583 "translate(" + 2584 x.toFixed(3) + 2585 "px ," + 2586 y.toFixed(3) + 2587 "px) scale(" + 2588 self.scale.toFixed(3) + 2589 ")", 2590 }); 2591 2592 if (scale) { 2593 $(".mapplic-pin, .mapplic-tooltip", self.map).css({ 2594 transition: "transform " + d + "s", 2595 transform: "scale(" + 1 / scale + ")", 2596 }); 2597 } 2598 2599 if (self.minimap) self.minimap.update(x, y); 2600 2601 // trigger event 2602 self.el.trigger("positionchanged", location); 2603 }; 2604 2605 var replaceVars = function (template, location) { 2606 template = template.replace(/\{\{([^}]+)\}\}/g, function (match) { 2607 match = match.slice(2, -2); 2608 var sub = match.split("."); 2609 if (sub.length > 1) { 2610 var temp = location; 2611 sub.forEach(function (item) { 2612 if (!temp[item]) { 2613 temp = "{{" + match + "}}"; 2614 return; 2615 } 2616 temp = temp[item]; 2617 }); 2618 return temp; 2619 } else { 2620 if (!location[match]) return "{{" + match + "}}"; 2621 return location[match]; 2622 } 2623 }); 2624 return template; 2625 }; 2626 2627 /* PUBLIC METHODS */ 2628 var levelTimeout = null; 2629 self.switchLevel = function (target) { 2630 // no such layer 2631 if (!target) return; 2632 2633 var layer = $('.mapplic-layer[data-floor="' + target + '"]', self.map); 2634 2635 // target layer is already active 2636 if (layer.hasClass("mapplic-visible")) return; 2637 2638 // show target layer 2639 var old = $(".mapplic-layer.mapplic-visible", self.map).removeClass( 2640 "mapplic-visible" 2641 ); 2642 2643 clearTimeout(levelTimeout); 2644 levelTimeout = setTimeout(function () { 2645 $('.mapplic-layer:not([data-floor="' + target + '"])', self.map).addClass( 2646 "mapplic-hidden" 2647 ); 2648 }, 300); 2649 layer.removeClass("mapplic-hidden").addClass("mapplic-visible"); 2650 2651 // slide animation 2652 if (self.o.animations) { 2653 var found = false;
2654 $(".mapplic-layer", self.map).each(function () { 2655 if ($(this).data("floor") == target) { 2656 $(this).removeClass("mapplic-layer-up").removeClass("mapplic-layer-down"); 2657 found = true; 2658 } else if (found) $(this).addClass("mapplic-layer-up"); 2659 else $(this).addClass("mapplic-layer-down"); 2660 }); 2661 } 2662 2663 // show target minimap layer 2664 if (self.minimap) self.minimap.show(target); 2665 2666 self.level = target; 2667 2668 // trigger event 2669 self.el.trigger("levelswitched", target); 2670 }; 2671 2672 self.mouseWheel = function (e, delta) { 2673 e.preventDefault(); 2674 self.container.stopMomentum(); 2675 2676 var scale = self.scale; 2677 2678 self.scale = normalizeScale(scale + (scale * delta) / 5); 2679 2680 self.x = normalizeX( 2681 self.x - 2682 (e.pageX - self.container.el.offset().left - self.x) * (self.scale / scale - 1) 2683 ); 2684 self.y = normalizeY( 2685 self.y - 2686 (e.pageY - self.container.el.offset().top - self.y) * (self.scale / scale - 1) 2687 ); 2688 2689 zoomTo(self.x, self.y, self.scale, 200, "ease"); 2690 }; 2691 2692 self.addTooltip = function (location, check) { 2693 var tooltip = new Tooltip().init(location, check); 2694 self.tooltips.push(tooltip); 2695 2696 return tooltip.wrap[0]; 2697 }; 2698 2699 self.closeTooltips = function () { 2700 self.tooltips.forEach(function (t, i) { 2701 t.hide(); 2702 self.tooltips.splice(i, 1); 2703 }); 2704 }; 2705 2706 self.moveTo = function (x, y, s, duration, ry) { 2707 duration = typeof duration !== "undefined" ? duration : 400; 2708 ry = typeof ry !== "undefined" ? ry : 0.5; 2709 s = typeof s !== "undefined" ? s : self.scale / self.fitscale; 2710 2711 self.container.stopMomentum(); 2712 2713 self.scale = normalizeScale(s); 2714 self.x = normalizeX( 2715 self.container.el.width() * 0.5 - self.scale * self.contentWidth * x 2716 ); 2717 self.y = normalizeY( 2718 self.container.el.height() * ry - self.scale * self.contentHeight * y 2719 ); 2720 2721 zoomTo(self.x, self.y, self.scale, duration); 2722 }; 2723 2724 self.bboxZoom = function (element) { 2725 var pos = estimatedPosition(element); 2726 if (!pos) return false; 2727 2728 self.moveTo(pos.x, pos.y, pos.scale, 600); 2729 return true; 2730 }; 2731 2732 // adding locations 2733 self.addLocations = function (locations, levelid) { 2734 $.each(locations, function (index, location) { 2735 if (location.id) self.addLocation(location, levelid); 2736 }); 2737 }; 2738 2739 self.addLocation = function (location, levelid) { 2740 // jump if location ID exists 2741 if (self.l[location.id]) return true; 2742 2743 // building the location object 2744 self.l[location.id] = location; 2745 2746 // groups 2747 var groups = location.category; 2748 if (groups) { 2749 if (typeof groups == "string") groups = groups.toString().split(","); 2750 groups.forEach(function (group) { 2751 if (self.g[group]) self.g[group].nr++; 2752 }); 2753 } 2754 2755 // cascaded fill 2756 var fill = 2757 (location && location.fill) || 2758 (location.category && 2759 self.g[location.category] && 2760 self.g[location.category].color) || 2761 (location.category && 2762 location.category[0] && 2763 self.g[location.category[0]] && 2764 self.g[location.category[0]].color) || 2765 self.o.fillcolor || 2766 false; 2767 2768 // cascade style to attribute styleD! 2769 location.styled = 2770 (location && location.style) || 2771 (location.category && 2772 self.g[location.category] && 2773 self.g[location.category].style) || 2774 (location.category && 2775 location.category[0] && 2776 self.g[location.category[0]] && 2777 self.g[location.category[0]].style) || 2778 self.o.defaultstyle || 2779 false; 2780 2781 if (fill) location.color = fill; 2782 else if (location.styled) location.color = location.styled.base; 2783 2784 // interactive element 2785 var elem = $( 2786 '[id^=MLOC] > *[id="' + 2787 location.id + 2788 '"], [id^=landmark] > *[id="' + 2789 location.id + 2790 '"]', 2791 self.map 2792 ); 2793 if (elem.length > 0) { 2794 location.el = elem; 2795 location.el.addClass("mapplic-clickable"); 2796 2797 if (location.styled) { 2798 location.el.addClass(location.styled); 2799 } else if (fill) { 2800 location.el.css("fill", fill); 2801 $("> *", location.el).css("fill", fill); 2802 } 2803 } 2804 2805 // first level if not set 2806 if (!location.level) { 2807 if (levelid) location.level = levelid; 2808 else if (location.el && location.el.closest(".mapplic-layer").data("floor")) 2809 location.level = location.el.closest(".mapplic-layer").data("floor"); 2810 else location.level = self.data.levels[0].id; 2811 } 2812 2813 // description vars 2814 if (location.description) 2815 location.description = replaceVars(location.description, location); 2816 2817 // geolocation 2818 if (location.lat && location.lng) { 2819 var pos = latlngToPos(location.lat, location.lng); 2820 location.x = pos.x; 2821 location.y = pos.y; 2822 } 2823 2824 // estimated position 2825 if ((!location.x || !location.y) && elem) { 2826 var pos = estimatedPosition(location.el); 2827 location.x = pos.x; 2828 location.y = pos.y; 2829 } 2830 2831 // marker 2832 if (!location.pin) location.pin = self.o.marker; 2833 location.marker = self.addMarker(location); 2834 2835 // reveal mode 2836 if (location.action == "reveal") 2837 $(".mapplic-pin[data-location^=" + location.id + "]", self.map).css( 2838 "visibility", 2839 "hidden" 2840 ); 2841 2842 if (self.sidebar) self.sidebar.countCategory(); 2843 }; 2844 2845 self.addMarker = function (location) { 2846 // hidden marker 2847 if (location.pin.indexOf("hidden") != -1) return false;
2848 2849 var parent = $( 2850 ".mapplic-layer[data-floor=" + location.level + "] .mapplic-map-image", 2851 self.el 2852 ); 2853 var marker = $("<a></a>") 2854 .addClass("mapplic-pin") 2855 .addClass(location.pin.replace("hidden", "")) 2856 .attr("aria-label", location.title + " marker") 2857 .css({ top: location.y * 100 + "%", left: location.x * 100 + "%" }) 2858 .appendTo(parent); 2859 marker.on("click touchend", function (e) { 2860 if (e.cancelable) e.preventDefault(); 2861 if (!self.dragging) self.showLocation(location.id, 600); 2862 }); 2863 2864 if (location.label) { 2865 if ( 2866 location.label.match( 2867 /(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/g 2868 ) 2869 ) 2870 marker.css("background-image", "url(" + location.label + ")"); 2871 else $("<span><span>" + location.label + "</span></span>").appendTo(marker); 2872 } 2873 if (location.reveal) 2874 marker.attr("data-reveal", location.reveal).css("visibility", "hidden"); 2875 if (location.category) { 2876 location.category = location.category.toString(); 2877 marker.attr("data-category", location.category); 2878 } 2879 marker.attr("data-location", location.id); 2880 2881 if (self.o.zoom && self.o.mousewheel) 2882 marker.on("mousewheel DOMMouseScroll", self.mouseWheel); 2883 if (location.styled) marker.addClass(location.styled); 2884 if (location.color && location.pin.indexOf("pin-text") > -1) 2885 marker.css("color", location.color); 2886 else if (location.color) 2887 marker.css({ 2888 "background-color": location.color, 2889 "border-color": location.color, 2890 }); 2891 2892 location.el = marker; 2893 return marker; 2894 }; 2895 2896 // removing locations 2897 self.removeLocations = function () { 2898 for (var key of Object.keys(self.l)) { 2899 self.removeLocation(key); 2900 } 2901 }; 2902 2903 self.removeLocation = function (location) { 2904 if (location.id) location = location.id; 2905 delete self.l[location]; 2906 $('.mapplic-pin[data-location="' + location + '"]', self.map).remove(); 2907 $('.mapplic-tooltip[data-location="' + location + '"]', self.el).remove(); 2908 $('.mapplic-list-location[data-location="' + location + '"]', self.el).remove(); 2909 $("svg #" + location, self.map).removeClass(); 2910 }; 2911 2912 self.getLocationData = function (id) { 2913 return self.l[id]; 2914 }; 2915 2916 self.showLocation = function (id, duration, check) { 2917 var location = (self.location = self.l[id]); 2918 if (!location) return false; 2919 2920 var action = 2921 location.action && location.action != "default" ? location.action : self.o.action; 2922 if (action == "disabled") return false; 2923 2924 // trigger event 2925 self.el.trigger("locationopen", location); 2926 2927 var content = null; 2928 self.closeTooltips(); 2929 2930 switch (action) { 2931 case "open-link": 2932 window.location.href = location.link; 2933 return false; 2934 case "open-link-new-tab": 2935 window.open(location.link); 2936 self.location = null; 2937 return false; 2938 case "select": 2939 if (location.el) { 2940 if (location.el.hasClass("mapplic-active")) { 2941 location.el.removeClass("mapplic-active"); 2942 if (location.list) location.list.removeClass("mapplic-active"); 2943 } else { 2944 location.el.addClass("mapplic-active"); 2945 if (location.list) location.list.addClass("mapplic-active"); 2946 } 2947 } 2948 return false; 2949 case "none": 2950 self.hideLocation(); 2951 self.switchLevel(location.level); 2952 if (!self.bboxZoom(location.el)) self.zoomLocation(location); 2953 break; 2954 case "reveal": 2955 self.hideLocation(); 2956 self.switchLevel(location.level); 2957 self.container.revealChild(location); 2958 if (self.o.zoom) self.bboxZoom(location.el); 2959 break; 2960 case "external": 2961 self.hideLocation(); 2962 self.switchLevel(location.level); 2963 if (!self.bboxZoom(location.el)) self.zoomLocation(location); 2964 if (self.external) self.external.show(location); 2965 break; 2966 case "lightbox": 2967 self.hideLocation(); 2968 self.switchLevel(location.level); 2969 content = self.lightbox.show(location); 2970 break; 2971 case "image": 2972 self.hideLocation(); 2973 self.switchLevel(location.level); 2974 self.lightbox.showImage(location); 2975 break; 2976 case "route": 2977 check = true; 2978 break; 2979 default: 2980 self.hideLocation(); 2981 self.switchLevel(location.level); 2982 content = self.addTooltip(location, check); 2983 } 2984 2985 self.location = self.l[id]; 2986 2987 // active state 2988 $(".mapplic-active", self.scope).removeClass("mapplic-active"); 2989 if (location.el) location.el.addClass("mapplic-active"); 2990 if (location.list) location.list.addClass("mapplic-active"); 2991 2992 // deeplinking 2993 if (self.deeplinking && !check) self.deeplinking.update(id); 2994 2995 // trigger event 2996 self.el.trigger("locationopened", [location, content]); 2997 }; 2998 2999 self.zoomLocation = function (loc) { 3000 var zoom = loc.zoom ? parseFloat(loc.zoom) / self.o.maxscale : 1; 3001 if (self.o.zoom) self.moveTo(loc.x, loc.y, zoom, 600); 3002 }; 3003 3004 self.hideLocation = function () { 3005 $(".mapplic-active", self.scope).removeClass("mapplic-active"); 3006 if (self.deeplinking && self.deeplinking.resolved) self.deeplinking.clear(); 3007 if (self.external) self.external.hide(); 3008 self.closeTooltips(); 3009 self.location = null; 3010 3011 // trigger event 3012 self.el.trigger("locationclosed"); 3013 }; 3014 3015 self.updateLocation = function (id) { 3016 // remove + add 3017 var location = self.l[id]; 3018 3019 if (location.id == id && location.el.is("a")) { 3020 // Geolocation 3021 if (location.lat && location.lng) { 3022 var pos = latlngToPos(location.lat, location.lng); 3023 location.x = pos.x; 3024 location.y = pos.y; 3025 } 3026 3027 var top = location.y * 100, 3028 left = location.x * 100; 3029 location.el.css({ top: top + "%", left: left + "%" }); 3030 } 3031 }; 3032 }; 3033 3034 // jQuery plugin 3035 $.fn.mapplic = function (options) { 3036 return this.each(function () { 3037 var element = $(this); 3038 3039 // plugin already initiated on element 3040 if (element.data("mapplic")) return; 3041 3042 var instance = new Mapplic(element).init(options); 3043 3044 // store plugin object in element's data 3045 element.data("mapplic", instance); 3046 }); 3047 }; 3048})(jQuery); 3049 3050// call plugin on map instances 3051jQuery(document).ready(function ($) { 3052 $("[id^=mapplic-id]").mapplic(); 3053 3054 // dynamic element 3055 var observer = new MutationObserver(function (mutations) { 3056 mutations.forEach(function (mutation) { 3057 var newNodes = mutation.addedNodes; 3058 if (newNodes !== null) { 3059 $(newNodes).each(function () { 3060 var node = $(this); 3061 if (node.is("[id^=mapplic-id]")) node.mapplic(); 3062 }); 3063 } 3064 }); 3065 }); 3066 3067 observer.observe(document, { 3068 attributes: false, 3069 childList: true, 3070 characterData: false, 3071 subtree: true, 3072 }); 3073});
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.