/* * Mapplic - Custom Interactive Map Plugin by @sekler * Version 4.1 * http://www.mapplic.com/ */ ;(function($) { "use strict"; var Mapplic = function(element) { var self = this; self.o = { source: 'locations.json', selector: '[id^=landmark] > *', landmark: false, mapfill: false, height: 420, markers: true, minimap: true, sidebar: true, search: true, deeplinking: true, clearbutton: true, zoombuttons: true, zoomoutclose: false, action: 'tooltip', lightbox: false, hovertip: true, smartip: true, fillcolor: '#4d5e6d', fullscreen: true, developer: false, mousewheel: true, alphabetic: false, maxscale: 3, zoom: true }; self.el = element; self.init = function(options) { // Merging options with defaults self.o = $.extend(self.o, options); self.el.addClass('mapplic-element mapplic-loading').height(self.o.height); if (typeof self.o.source === 'string') { // Loading .json file with AJAX $.getJSON(self.o.source, function(data) { // Success processData(data); self.el.removeClass('mapplic-loading'); }).fail(function() { // Failure: couldn't load JSON file or it is invalid. console.error('Couldn\'t load map data. (Make sure to run the script through web server)'); self.el.removeClass('mapplic-loading').addClass('mapplic-error'); alert('Data file missing or invalid!\nMake sure to run the script through web server.'); }); } else { // Inline json object processData(self.o.source); self.el.removeClass('mapplic-loading'); } return self; } // Tooltip function Tooltip() { this.el = null; this.pin = null; this.shift = 6; this.drop = 0; this.location = null; this.tooltipthumb = true; this.tooltiplink = true; this.init = function() { var s = this; // Construct this.el = $('
').addClass('mapplic-tooltip'); this.close = $('').addClass('mapplic-tooltip-close').attr('href', '#').appendTo(this.el); this.close.on('click touchend', function(e) { e.preventDefault(); self.hideLocation(); if (!self.o.zoom || self.o.zoomoutclose) self.moveTo(0.5, 0.5, self.fitscale, 400, 'easeInOutCubic'); }); if (this.tooltipthumb) this.thumbnail = $('').addClass('mapplic-tooltip-thumbnail').hide().appendTo(this.el); this.content = $('
').addClass('mapplic-tooltip-content').appendTo(this.el); this.title = $('

').addClass('mapplic-tooltip-title').appendTo(this.content); this.desc = $('
').addClass('mapplic-tooltip-description').appendTo(this.content); if (this.tooltiplink) this.link = $('More').addClass('mapplic-popup-link').attr('href', '#').hide().appendTo(this.el); this.triangle = $('
').addClass('mapplic-tooltip-triangle').prependTo(this.el); // Append self.map.append(this.el); return this; } this.show = function(location) { if (location) { var s = this; this.location = location; if (self.hovertip) self.hovertip.hide(); if (this.tooltipthumb) { if (location.thumbnail) this.thumbnail.attr('src', location.thumbnail).show(); else this.thumbnail.hide(); } if (this.tooltiplink) { if (location.link) this.link.attr('href', location.link).show(); else this.link.hide(); } this.title.text(location.title); if (location.description) this.desc.html(location.description); else this.desc.empty(); this.content[0].scrollTop = 0; // Shift this.pin = $('.mapplic-pin[data-location="' + location.id + '"]'); if (this.pin.length == 0) { this.shift = 20; } else this.shift = Math.abs(parseFloat(this.pin.css('margin-top'))) + 20; // Loading & positioning $('img', this.el).off('load').on('load', function() { s.position(); s.zoom(location); }); this.position(); // Making it visible this.el.stop().show(); this.zoom(location); } } this.position = function() { if (this.location) { var cx = self.map.offset().left + self.map.width() * this.location.x - self.container.offset().left, cy = self.map.offset().top + self.map.height() * this.location.y - self.container.offset().top; var x = this.location.x * 100, y = this.location.y * 100, mt = -this.el.outerHeight() - this.shift, ml = -this.el.outerWidth() / 2; if (self.o.smartip) { var verticalPos = 0.5; // Top check if (Math.abs(mt) > cy) { mt = 8 + 2; if (this.pin && this.pin.length) mt = this.pin.height() + parseFloat(this.pin.css('margin-top')) + 20; this.el.addClass('mapplic-bottom'); } else this.el.removeClass('mapplic-bottom'); // Left-right check if (this.el.outerWidth()/2 > cx) verticalPos = 0.5 - (this.el.outerWidth()/2 - cx)/this.el.outerWidth(); else if ((self.container.width() - cx - this.el.outerWidth()/2) < 0) verticalPos = 0.5 + (cx + this.el.outerWidth()/2 - self.container.width())/this.el.outerWidth(); verticalPos = Math.max(0, Math.min(1, verticalPos)); ml = -this.el.outerWidth() * verticalPos; this.triangle.css('left', Math.max(5, Math.min(95, verticalPos * 100)) + '%'); } this.el.css({ left: x + '%', top: y + '%', marginTop: mt, marginLeft: ml }); this.drop = this.el.outerHeight() + this.shift; } } this.zoom = function(location) { var ry = 0.5, zoom = location.zoom ? parseFloat(location.zoom) : self.o.maxscale; ry = ((self.container.height() - this.drop) / 2 + this.drop) / self.container.height(); self.moveTo(location.x, location.y, zoom, 600, 'easeInOutCubic', ry); } this.hide = function() { var s = this; this.location = null; this.el.stop().fadeOut(300, function() { if (s.desc) s.desc.empty(); }); } } // Lightbox function Lightbox() { this.el = null; this.init = function() { // Construct this.el = $('
').addClass('mapplic-lightbox mfp-hide'); this.title = $('

').addClass('mapplic-lightbox-title').appendTo(this.el); this.desc = $('
').addClass('mapplic-lightbox-description').appendTo(this.el); this.link = $('More').addClass('mapplic-popup-link').attr('href', '#').hide().appendTo(this.el); // Popup Image $('body').magnificPopup({ delegate: '.mapplic-popup-image', type: 'image', removalDelay: 300, mainClass: 'mfp-fade' }); // Append self.el.append(this.el); return this; } this.show = function(location) { this.location = location; this.title.text(location.title); this.desc.html(location.description); if (location.link) this.link.attr('href', location.link).show(); else this.link.hide(); var s = this; $.magnificPopup.open({ items: { src: this.el }, type: 'inline', removalDelay: 300, mainClass: 'mfp-fade', callbacks: { beforeClose: function() { s.hide(); } } }); // Zoom var zoom = location.zoom ? parseFloat(location.zoom) : self.o.maxscale; self.moveTo(location.x, location.y, zoom, 600, 'easeInOutCubic'); // Hide tooltip if (self.tooltip) self.tooltip.hide(); } this.hide = function() { this.location = null; self.hideLocation(); if (!self.o.zoom || self.o.zoomoutclose) self.moveTo(0.5, 0.5, self.fitscale, 400, 'easeInOutCubic'); } } // HoverTooltip function HoverTooltip() { this.el = null; this.pin = null; this.shift = 6; this.hovertipdesc = false; this.init = function() { var s = this; // Construct this.el = $('
').addClass('mapplic-tooltip mapplic-hovertip'); this.title = $('

').addClass('mapplic-tooltip-title').appendTo(this.el); if (this.hovertipdesc) this.desc = $('
').addClass('mapplic-tooltip-description').appendTo(this.el); this.triangle = $('
').addClass('mapplic-tooltip-triangle').appendTo(this.el); // Events // pins + old svg $(self.map).on('mouseover', '.mapplic-layer a', function() { var id = ''; if ($(this).hasClass('mapplic-pin')) { id = $(this).data('location'); s.pin = $('.mapplic-pin[data-location="' + id + '"]'); s.shift = Math.abs(parseFloat(s.pin.css('margin-top'))) + 20; } else { id = $(this).attr('xlink:href').slice(1); s.shift = 20; } var location = self.getLocationData(id); if (location) s.show(location); }).on('mouseout', function() { s.hide(); }); // new svg if (self.o.selector) { $(self.map).on('mouseover', self.o.selector, function() { var location = self.getLocationData($(this).attr('id')); s.shift = 20; if (location) s.show(location); }).on('mouseout', function() { s.hide(); }); } self.map.append(this.el); return this; } this.show = function(location) { if (self.tooltip.location != location) { this.title.text(location.title); if (this.hovertipdesc) this.desc.html(location.description); this.position(location); this.el.stop().fadeIn(100); } } this.position = function(location) { var cx = self.map.offset().left + self.map.width() * location.x - self.container.offset().left, cy = self.map.offset().top + self.map.height() * location.y - self.container.offset().top; var x = location.x * 100, y = location.y * 100, mt = -this.el.outerHeight() - this.shift, ml = 0; var verticalPos = 0.5; // Top check if (Math.abs(mt) > cy) { mt = 8 + 2; if (this.pin && this.pin.length) mt = this.pin.height() + parseFloat(this.pin.css('margin-top')) + 20; this.el.addClass('mapplic-bottom'); } else this.el.removeClass('mapplic-bottom'); // Left-right check if (this.el.outerWidth()/2 > cx) verticalPos = 0.5 - (this.el.outerWidth()/2 - cx)/this.el.outerWidth(); else if ((self.container.width() - cx - this.el.outerWidth()/2) < 0) verticalPos = 0.5 + (cx + this.el.outerWidth()/2 - self.container.width())/this.el.outerWidth(); ml = -this.el.outerWidth() * verticalPos; this.triangle.css('left', Math.max(10, Math.min(90, verticalPos * 100)) + '%'); this.el.css({ left: x + '%', top: y + '%', marginTop: mt, marginLeft: ml }); } this.hide = function() { this.el.stop().fadeOut(200); } } // Deeplinking function Deeplinking() { this.param = 'location'; this.init = function() { var s = this; this.check(0); window.onpopstate = function(e) { if (e.state) { s.check(600); } return false; } } this.check = function(easing) { var id = this.getUrlParam(this.param); self.showLocation(id, easing, true); } this.getUrlParam = function(name) { name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), results = regex.exec(location.search); return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); } this.update = function(id) { var url = window.location.protocol + "//" + window.location.host + window.location.pathname + '?' + this.param + '=' + id; window.history.pushState({path: url}, '', url); } // Clear this.clear = function() { history.pushState('', document.title, window.location.pathname); } } // Old hash deeplinking method for old browsers function DeeplinkingHash() { this.param = 'location'; this.init = function() { var s = this; this.check(0); $(window).on('hashchange', function() { s.check(600); }); } this.check = function(easing) { var id = location.hash.slice(this.param.length + 2); self.showLocation(id, easing, true); } this.update = function(id) { window.location.hash = this.param + '-' + id; } this.clear = function() { window.location.hash = this.param; } } // Minimap function Minimap() { this.el = null; this.opacity = null; this.init = function() { this.el = $('
').addClass('mapplic-minimap').appendTo(self.container); this.el.click(function(e) { e.preventDefault(); var x = (e.pageX - $(this).offset().left) / $(this).width(), y = (e.pageY - $(this).offset().top) / $(this).height(); self.moveTo(x, y, self.scale / self.fitscale, 100); }); } this.addLayer = function(data) { var layer = $('
').addClass('mapplic-minimap-layer').attr('data-floor', data.id).appendTo(this.el), s = this; $('').attr('src', data.minimap).addClass('mapplic-minimap-background').appendTo(layer); $('
').addClass('mapplic-minimap-overlay').appendTo(layer); $('').attr('src', data.minimap).addClass('mapplic-minimap-active').on('load', function() { s.update(); $(this).addClass('mapplic-clip-transition'); }).appendTo(layer); } this.show = function(target) { $('.mapplic-minimap-layer', this.el).hide(); $('.mapplic-minimap-layer[data-floor="' + target + '"]', this.el).show(); } this.update = function(x, y) { var active = $('.mapplic-minimap-active', this.el); if (x === undefined) x = self.x; if (y === undefined) y = self.y; var width = Math.round(self.container.width() / self.contentWidth / self.scale * this.el.width()), height = Math.round(self.container.height() / self.contentHeight / self.scale * this.el.height()), top = Math.round(-y / self.contentHeight / self.scale * this.el.height()), left = Math.round(-x / self.contentWidth / self.scale * this.el.width()), right = left + width, bottom = top + height; active.each(function() { $(this)[0].style.clip = 'rect(' + top + 'px, ' + right + 'px, ' + bottom + 'px, ' + left + 'px)'; }); // Fade out effect var s = this; this.el.show(); this.el.css('opacity', 1.0); clearTimeout(this.opacity); this.opacity = setTimeout(function() { s.el.css('opacity', 0); setTimeout(function() { s.el.hide(); }, 600); }, 2000); } } // Sidebar function Sidebar() { this.el = null; this.list = null; this.init = function() { var s = this; this.el = $('
').addClass('mapplic-sidebar').appendTo(self.el); if (self.o.search) { var form = $('
').addClass('mapplic-search-form').submit(function() { return false; }).appendTo(this.el); self.clear = $('').attr('type', 'button').addClass('mapplic-search-clear').click(function() { input.val(''); input.keyup(); }).appendTo(form); var input = $('').attr({'type': 'text', 'spellcheck': 'false', 'placeholder': 'Search...'}).addClass('mapplic-search-input').keyup(function() { var keyword = $(this).val(); s.search(keyword); }).prependTo(form); } var listContainer = $('
').addClass('mapplic-list-container').appendTo(this.el); this.list = $('
    ').addClass('mapplic-list').appendTo(listContainer); this.notfound = $('

    ').addClass('mapplic-not-found').text('Nothing found. Please try a different search.').appendTo(listContainer); if (!self.o.search) listContainer.css('padding-top', '0'); } this.addCategories = function(categories) { var list = this.list; if (categories) { $.each(categories, function(index, category) { var item = $('
  1. ').addClass('mapplic-list-category').attr('data-category', category.id); var ol = $('
      ').css('border-color', category.color).appendTo(item); if (category.show == 'false') ol.hide(); else item.addClass('mapplic-opened'); var link = $('').attr('href', '#').attr('title', category.title).css('background-color', category.color).text(category.title).prependTo(item); link.on('click', function(e) { e.preventDefault(); item.toggleClass('mapplic-opened'); ol.slideToggle(200); }); if (category.icon) $('').attr('src', category.icon).addClass('mapplic-list-thumbnail').prependTo(link); $('').text('0').addClass('mapplic-list-count').prependTo(link); list.append(item); }); } } this.addLocation = function(data) { var item = $('
    1. ').addClass('mapplic-list-location').addClass('mapplic-list-shown').attr('data-location', data.id); var link = $('').attr('href', '#').click(function(e) { e.preventDefault(); self.showLocation(data.id, 600); // Scroll back to map on mobile if ($(window).width() < 668) { $('html, body').animate({ scrollTop: self.container.offset().top }, 400); } }).appendTo(item); if (data.thumbnail) $('').attr('src', data.thumbnail).addClass('mapplic-list-thumbnail').appendTo(link); $('

      ').text(data.title).appendTo(link) $('').html(data.about).appendTo(link); var category = $('.mapplic-list-category[data-category="' + data.category + '"]'); if (category.length) $('ol', category).append(item); else this.list.append(item); // Count $('.mapplic-list-count', category).text($('.mapplic-list-shown', category).length); } this.search = function(keyword) { if (keyword) self.clear.fadeIn(100); else self.clear.fadeOut(100); $('.mapplic-list li', self.el).each(function() { if ($(this).text().search(new RegExp(keyword, "i")) < 0) { $(this).removeClass('mapplic-list-shown'); $(this).slideUp(200); } else { $(this).addClass('mapplic-list-shown'); $(this).show(); } }); $('.mapplic-list > li', self.el).each(function() { var count = $('.mapplic-list-shown', this).length; $('.mapplic-list-count', this).text(count); }); // Show not-found text if ($('.mapplic-list > li.mapplic-list-shown').length > 0) this.notfound.fadeOut(200); else this.notfound.fadeIn(200); } } // Developer tools function DevTools() { this.el = null; this.init = function() { this.el = $('
      ').addClass('mapplic-coordinates').appendTo(self.container); this.el.append('x: '); $('').addClass('mapplic-coordinates-x').appendTo(this.el); this.el.append(' y: '); $('').addClass('mapplic-coordinates-y').appendTo(this.el); $('.mapplic-layer', self.map).on('mousemove', function(e) { var x = (e.pageX - self.map.offset().left) / self.map.width(), y = (e.pageY - self.map.offset().top) / self.map.height(); $('.mapplic-coordinates-x').text(parseFloat(x).toFixed(4)); $('.mapplic-coordinates-y').text(parseFloat(y).toFixed(4)); }); return this; } } // Clear Button function ClearButton() { this.el = null; this.init = function() { this.el = $('').attr('href', '#').addClass('mapplic-clear-button').appendTo(self.container); this.el.on('click touchstart', function(e) { e.preventDefault(); self.hideLocation(); self.moveTo(0.5, 0.5, self.fitscale, 400, 'easeInOutCubic'); }); return this; } } // Zoom Buttons function ZoomButtons() { this.el = null; this.init = function() { this.el = $('
      ').addClass('mapplic-zoom-buttons').appendTo(self.container); if (!self.o.clearbutton) this.el.css('bottom', '0'); // Zoom in button this.zoomin = $('').attr('href', '#').addClass('mapplic-zoomin-button').appendTo(this.el); this.zoomin.on('click touchstart', function(e) { e.preventDefault(); var scale = self.scale; self.scale = normalizeScale(scale + scale * 0.8); self.x = normalizeX(self.x - (self.container.width() / 2 - self.x) * (self.scale / scale - 1)); self.y = normalizeY(self.y - (self.container.height() / 2 - self.y) * (self.scale / scale - 1)); zoomTo(self.x, self.y, self.scale, 400, 'easeInOutCubic'); }); // Zoom out button this.zoomout = $('').attr('href', '#').addClass('mapplic-zoomout-button').appendTo(this.el); this.zoomout.on('click touchstart', function(e) { e.preventDefault(); var scale = self.scale; self.scale = normalizeScale(scale - scale * 0.4); self.x = normalizeX(self.x - (self.container.width() / 2 - self.x) * (self.scale / scale - 1)); self.y = normalizeY(self.y - (self.container.height() / 2 - self.y) * (self.scale / scale - 1)); zoomTo(self.x, self.y, self.scale, 400, 'easeInOutCubic'); }); return this; } this.update = function(scale) { this.zoomin.removeClass('mapplic-disabled'); this.zoomout.removeClass('mapplic-disabled'); if (scale == self.fitscale) this.zoomout.addClass('mapplic-disabled'); else if (scale == self.o.maxscale) this.zoomin.addClass('mapplic-disabled'); } } // Fullscreen function Fullscreen() { this.el = null; this.init = function() { // Fullscreen Button $('').attr('href', '#').attr('href', '#').addClass('mapplic-fullscreen-button').click(function(e) { e.preventDefault(); self.el.toggleClass('mapplic-fullscreen'); $(document).resize(); }).appendTo(self.container); // Esc key $(document).keyup(function(e) { if (e.keyCode === 27) { $('.mapplic-element.mapplic-fullscreen').removeClass('mapplic-fullscreen'); $(document).resize(); } }); } } // Functions var processData = function(data) { self.data = data; var shownLevel = null; // Disable modules when landmark mode is active self.o.landmark = self.el.data('landmark'); if (self.o.landmark) { self.o.sidebar = false; self.o.zoombuttons = false; self.o.deeplinking = false; } self.container = $('
      ').addClass('mapplic-container').appendTo(self.el); self.map = $('
      ').addClass('mapplic-map').appendTo(self.container); if (self.o.zoom) self.map.addClass('mapplic-zoomable'); self.levelselect = $('').addClass('mapplic-levels-select'); self.contentWidth = parseFloat(data.mapwidth); self.contentHeight = parseFloat(data.mapheight); // Create minimap if (self.o.minimap) { self.minimap = new Minimap(); self.minimap.init(); } // Create sidebar if (self.o.sidebar) { self.sidebar = new Sidebar(); self.sidebar.init(); self.sidebar.addCategories(data.categories); } else self.container.css('width', '100%'); // Iterate through levels var nrlevels = 0; if (data.levels) { $.each(data.levels, function(index, level) { var source = level.map, extension = source.substr((source.lastIndexOf('.') + 1)).toLowerCase(); // Create new map layer var layer = $('
      ').addClass('mapplic-layer').attr('data-floor', level.id).hide().appendTo(self.map); switch (extension) { // Image formats case 'jpg': case 'jpeg': case 'png': case 'gif': $('').attr('src', source).addClass('mapplic-map-image').appendTo(layer); break; // Vector format case 'svg': $('
      ').addClass('mapplic-map-image').load(source, function() { // Setting up the locations on the map $(self.o.selector, this).each(function() { var location = self.getLocationData($(this).attr('id')); if (location) { $(this).attr('class', 'mapplic-clickable'); location.el = $(this); var fill = null; if (location.fill) fill = location.fill; else if (self.o.fillcolor) fill = self.o.fillcolor; if (fill) { $(this).css('fill', fill); $('> *', this).css('fill', fill); } // Landmark mode if (self.o.landmark === location.id) $(this).attr('class', 'mapplic-active'); } }); // Click event $(self.o.selector, this).on('click touchend', function() { if (!self.dragging) self.showLocation($(this).attr('id'), 600); }); // Support for the old map format $('svg a', this).each(function() { var location = self.getLocationData($(this).attr('xlink:href').substr(1)); if (location) { $(this).attr('class', 'mapplic-clickable'); location.el = $(this); } }); $('svg a', this).click(function(e) { var id = $(this).attr('xlink:href').substr(1); self.showLocation(id, 600); e.preventDefault(); }); }).appendTo(layer); break; // Other default: alert('File type ' + extension + ' is not supported!'); } // Create new minimap layer if (self.minimap) self.minimap.addLayer(level); // Build layer control self.levelselect.prepend($('').attr('value', level.id).text(level.title)); // Shown level if (!shownLevel || level.show) shownLevel = level.id; // Iterate through locations $.each(level.locations, function(index, location) { // Geolocation if (location.lat && location.lng) { var pos = latlngToPos(location.lat, location.lng); location.x = pos.x; location.y = pos.y; } var top = location.y * 100, left = location.x * 100; if (!location.pin) location.pin = 'default'; if (location.pin.indexOf('hidden') == -1) { if (self.o.markers) { var pin = $('').attr('href', '#').addClass('mapplic-pin').css({'top': top + '%', 'left': left + '%'}).appendTo(layer); pin.on('click touchend', function(e) { e.preventDefault(); self.showLocation(location.id, 600); }); if (location.label) pin.html(location.label); if (location.fill) pin.css('background-color', location.fill); pin.attr('data-location', location.id); pin.addClass(location.pin); location.el = pin; } } if (self.sidebar) self.sidebar.addLocation(location); }); nrlevels++; }); } // COMPONENTS // Tooltip (default) self.tooltip = new Tooltip().init(); // Lightbox if (self.o.lightbox) self.lightbox = new Lightbox().init(); // Hover Tooltip if (self.o.hovertip) self.hovertip = new HoverTooltip().init(); // Clear button if (self.o.clearbutton) self.clearbutton = new ClearButton().init(); // Zoom buttons if (self.o.zoombuttons) self.zoombuttons = new ZoomButtons().init(); // Fullscreen if (self.o.fullscreen) self.fullscreen = new Fullscreen().init(); // Developer tools if (self.o.developer) self.devtools = new DevTools().init(); console.log(self.devtools); // Level switcher if (nrlevels > 1) { self.levels = $('
      ').addClass('mapplic-levels'); var up = $('').addClass('mapplic-levels-up').appendTo(self.levels); self.levelselect.appendTo(self.levels); var down = $('').addClass('mapplic-levels-down').appendTo(self.levels); self.container.append(self.levels); self.levelselect.change(function() { var value = $(this).val(); self.switchLevel(value); }); up.click(function(e) { e.preventDefault(); if (!$(this).hasClass('mapplic-disabled')) self.switchLevel('+'); }); down.click(function(e) { e.preventDefault(); if (!$(this).hasClass('mapplic-disabled')) self.switchLevel('-'); }); } self.switchLevel(shownLevel); // Browser resize $(window).resize(function() { // Mobile if ($(window).width() < 668) { if (self.el.hasClass('mapplic-fullscreen')) self.container.height($(window).height()); else { var height = Math.min(Math.max(self.container.width() * self.contentHeight / self.contentWidth, $(window).height() * 2/3), $(window).height() - 66); self.container.height(height); } } else self.container.height('100%'); var wr = self.container.width() / self.contentWidth, hr = self.container.height() / self.contentHeight; if (self.o.mapfill) { if (wr > hr) self.fitscale = wr; else self.fitscale = hr; } else { if (wr < hr) self.fitscale = wr; else self.fitscale = hr; } //if (!self.data.zoomlimit) self.o.maxscale = self.fitscale; self.scale = normalizeScale(self.scale); self.x = normalizeX(self.x); self.y = normalizeY(self.y); zoomTo(self.x, self.y, self.scale, 0); }).resize(); // Landmark mode if (self.o.landmark) { self.showLocation(self.o.landmark, 0); } else { var init = self.getLocationData('init'); // Landmark with ID 'init' if (init) self.moveTo(init.x, init.y, parseFloat(init.zoom), 0); else self.moveTo(0.5, 0.5, self.fitscale, 0); } // Deeplinking if (self.o.deeplinking) { if (history.pushState) self.deeplinking = new Deeplinking(); else self.deeplinking = new DeeplinkingHash(); self.deeplinking.init(); } // Trigger event self.el.trigger('mapready', self); // Controls if (self.o.zoom) addControls(); } var addControls = function() { var map = self.map, mapbody = $('.mapplic-map-image', self.map); document.ondragstart = function() { return false; } // IE drag fix // Drag & drop mapbody.on('mousedown', function(e) { self.dragging = false; map.data('mouseX', e.pageX); map.data('mouseY', e.pageY); if (map.is(':animated')) { map.stop(); self.x = normalizeX(map.offset().left - self.container.offset().left); self.y = normalizeY(map.offset().top - self.container.offset().top); self.scale = normalizeScale(map.width() / self.contentWidth); } map.addClass('mapplic-dragging'); self.map.on('mousemove', function(e) { self.dragging = true; var x = e.pageX - map.data('mouseX') + self.x, y = e.pageY - map.data('mouseY') + self.y; x = normalizeX(x); y = normalizeY(y); zoomTo(x, y); map.data('lastX', x); map.data('lastY', y); }); $(document).on('mouseup', function() { self.x = map.data('lastX'); self.y = map.data('lastY'); self.map.off('mousemove'); $(document).off('mouseup'); map.removeClass('mapplic-dragging'); }); }); // Double click self.el.on('dblclick', '.mapplic-map-image', function(e) { e.preventDefault(); var scale = self.scale; self.scale = normalizeScale(scale * 2); self.x = normalizeX(self.x - (e.pageX - self.container.offset().left - self.x) * (self.scale/scale - 1)); self.y = normalizeY(self.y - (e.pageY - self.container.offset().top - self.y) * (self.scale/scale - 1)); zoomTo(self.x, self.y, self.scale, 400, 'easeInOutCubic'); }); // Mousewheel if (self.o.mousewheel) { $('.mapplic-layer', self.el).bind('mousewheel DOMMouseScroll', function(e, delta) { var scale = self.scale; self.scale = normalizeScale(scale + scale * delta / 5); // Disable page scroll when zoom is applicable //if (scale != self.scale) e.preventDefault(); self.x = normalizeX(self.x - (e.pageX - self.container.offset().left - self.x) * (self.scale/scale - 1)); self.y = normalizeY(self.y - (e.pageY - self.container.offset().top - self.y) * (self.scale/scale - 1)); zoomTo(self.x, self.y, self.scale, 200, 'easeOutCubic'); }); } // Touch support if (!('ontouchstart' in window || 'onmsgesturechange' in window)) return true; mapbody.on('touchstart', function(e) { self.dragging = false; var orig = e.originalEvent, pos = map.position(); map.data('touchY', orig.changedTouches[0].pageY - pos.top); map.data('touchX', orig.changedTouches[0].pageX - pos.left); mapbody.on('touchmove', function(e) { e.preventDefault(); self.dragging = true; var orig = e.originalEvent; var touches = orig.touches.length; if (touches == 1) { self.x = normalizeX(orig.changedTouches[0].pageX - map.data('touchX')); self.y = normalizeY(orig.changedTouches[0].pageY - map.data('touchY')); zoomTo(self.x, self.y, self.scale, 50); } else { mapbody.off('touchmove'); } }); mapbody.on('touchend', function(e) { mapbody.off('touchmove touchend'); }); }); // Pinch zoom var hammer = new Hammer(self.map[0], { transform_always_block: true, drag_block_horizontal: true, drag_block_vertical: true }); hammer.get('pinch').set({ enable: true }); var scale = 1, last_scale; hammer.on('pinchstart', function(e) { self.dragging = false; scale = self.scale / self.fitscale; last_scale = scale; }); hammer.on('pinch', function(e) { self.dragging = true; if (e.scale != 1) scale = Math.max(1, Math.min(last_scale * e.scale, 100)); var oldscale = self.scale; self.scale = normalizeScale(scale * self.fitscale); self.x = normalizeX(self.x - (e.center.x - self.container.offset().left - self.x) * (self.scale / oldscale - 1)); self.y = normalizeY(self.y - (e.center.y - self.y) * (self.scale / oldscale - 1)); // - self.container.offset().top zoomTo(self.x, self.y, self.scale, 100); }); } /* PRIVATE METHODS */ // Web Mercator (EPSG:3857) lat/lng projection var latlngToPos = function(lat, lng) { var deltaLng = self.data.rightLng - self.data.leftLng, bottomLatDegree = self.data.bottomLat * Math.PI / 180, mapWidth = ((self.data.mapwidth / deltaLng) * 360) / (2 * Math.PI), mapOffsetY = (mapWidth / 2 * Math.log((1 + Math.sin(bottomLatDegree)) / (1 - Math.sin(bottomLatDegree)))); lat = lat * Math.PI / 180; return { x: ((lng - self.data.leftLng) * (self.data.mapwidth / deltaLng)) / self.data.mapwidth, y: (self.data.mapheight - ((mapWidth / 2 * Math.log((1 + Math.sin(lat)) / (1 - Math.sin(lat)))) - mapOffsetY)) / self.data.mapheight }; } // jQuery bug add/remove class workaround (will be fixed in jQuery 3) var addClass = function(element, c) { var classes = element.attr('class'); if (classes.indexOf(c) == -1) element.attr('class', classes + ' ' + c); } var removeClass = function(element, c) { var classes = element.attr('class'); if (classes) element.attr('class', classes.replace(c, '').trim()); } var hasClass = function(element, c) { var classes = element.attr('class'); return (classes.indexOf(c) > -1); } // Normalizing x, y and scale var normalizeX = function(x) { var minX = self.container.width() - self.contentWidth * self.scale; if (minX < 0) { if (x > 0) x = 0; else if (x < minX) x = minX; } else x = minX/2; return x; } var normalizeY = function(y) { var minY = self.container.height() - self.contentHeight * self.scale; if (minY < 0) { if (y >= 0) y = 0; else if (y < minY) y = minY; } else y = minY/2; return y; } var normalizeScale = function(scale) { if (scale < self.fitscale) scale = self.fitscale; else if (scale > self.o.maxscale) scale = self.o.maxscale; if (self.zoombuttons) self.zoombuttons.update(scale); return scale; } var zoomTo = function(x, y, scale, d, easing) { if (scale !== undefined) { self.map.stop().animate({ 'left': x, 'top': y, 'width': self.contentWidth * scale, 'height': self.contentHeight * scale }, d, easing, function() { if (self.tooltip) self.tooltip.position(); }); } else { self.map.css({ 'left': x, 'top': y }); } if (self.tooltip) self.tooltip.position(); if (self.minimap) self.minimap.update(x, y); // Trigger event self.el.trigger('positionchanged', location); } /* PUBLIC METHODS */ self.switchLevel = function(target) { switch (target) { case '+': target = $('option:selected', self.levelselect).removeAttr('selected').prev().prop('selected', 'selected').val(); break; case '-': target = $('option:selected', self.levelselect).removeAttr('selected').next().prop('selected', 'selected').val(); break; default: $('option[value="' + target + '"]', self.levelselect).prop('selected', 'selected'); } // No such layer if (!target) return; var layer = $('.mapplic-layer[data-floor="' + target + '"]', self.el); // Target layer is already active if (layer.is(':visible')) return; // Hide Tooltip if (self.tooltip) self.tooltip.hide(); // Show target layer $('.mapplic-layer:visible', self.map).hide(); layer.show(); // Show target minimap layer if (self.minimap) self.minimap.show(target); // Update control var index = self.levelselect.get(0).selectedIndex, up = $('.mapplic-levels-up', self.el), down = $('.mapplic-levels-down', self.el); up.removeClass('mapplic-disabled'); down.removeClass('mapplic-disabled'); if (index == 0) up.addClass('mapplic-disabled'); else if (index == self.levelselect.get(0).length - 1) down.addClass('mapplic-disabled'); // Trigger event self.el.trigger('levelswitched', target); } self.moveTo = function(x, y, s, duration, easing, ry) { duration = typeof duration !== 'undefined' ? duration : 400; ry = typeof ry !== 'undefined' ? ry : 0.5; s = typeof s !== 'undefined' ? s : self.scale/self.fitscale; self.scale = normalizeScale(s); self.x = normalizeX(self.container.width() * 0.5 - self.scale * self.contentWidth * x); self.y = normalizeY(self.container.height() * ry - self.scale * self.contentHeight * y); zoomTo(self.x, self.y, self.scale, duration, easing); } self.getLocationData = function(id) { var data = null; $.each(self.data.levels, function(index, level) { $.each(level.locations, function(index, location) { if (location.id == id) { data = location; } }); }); return data; } self.showLocation = function(id, duration, check) { $.each(self.data.levels, function(index, level) { if (level.id == id) { self.switchLevel(level.id); return false; } $.each(level.locations, function(index, location) { if (location.id == id) { var action = (location.action && location.action != 'default') ? location.action : self.o.action; switch (action) { case 'open-link': window.location.href = location.link; return false; case 'open-link-new-tab': window.open(location.link); return false; case 'select': if (location.el) { if (hasClass(location.el, 'mapplic-active')) removeClass(location.el, 'mapplic-active'); else addClass(location.el, 'mapplic-active'); } return false; case 'none': var zoom = location.zoom ? parseFloat(location.zoom) : self.o.maxscale; self.switchLevel(level.id); self.moveTo(location.x, location.y, zoom, 600, 'easeInOutCubic'); break; case 'lightbox': self.switchLevel(level.id); self.lightbox.show(location); break; default: self.switchLevel(level.id); self.tooltip.show(location); } // Active state removeClass($('.mapplic-active', self.el), 'mapplic-active'); if (location.el) addClass(location.el, 'mapplic-active'); // Deeplinking if ((self.deeplinking) && (!check)) self.deeplinking.update(id); // Trigger event self.el.trigger('locationopened', location); } }); }); } self.hideLocation = function() { removeClass($('.mapplic-active', self.el), 'mapplic-active'); if (self.deeplinking) self.deeplinking.clear(); if (self.tooltip) self.tooltip.hide(); // Trigger event self.el.trigger('locationclosed'); } self.updateLocation = function(id) { var location = self.getLocationData(id); if ((location.id == id) && (location.el.is('a'))) { // Geolocation if (location.lat && location.lng) { var pos = latlngToPos(location.lat, location.lng); location.x = pos.x; location.y = pos.y; } var top = location.y * 100, left = location.x * 100; location.el.css({'top': top + '%', 'left': left + '%'}); } } }; // Easing functions used by default // For the full list of easing functions use jQuery Easing Plugin $.extend($.easing, { def: 'easeOutQuad', swing: function (x, t, b, c, d) { //alert(jQuery.easing.default); return jQuery.easing[jQuery.easing.def](x, t, b, c, d); }, easeOutQuad: function (x, t, b, c, d) { return -c *(t/=d)*(t-2) + b; }, easeOutCubic: function (x, t, b, c, d) { return c*((t=t/d-1)*t*t + 1) + b; }, easeInOutCubic: function (x, t, b, c, d) { if ((t/=d/2) < 1) return c/2*t*t*t + b; return c/2*((t-=2)*t*t + 2) + b; } }); // jQuery Plugin $.fn.mapplic = function(options) { return this.each(function() { var element = $(this); // Plugin already initiated on element if (element.data('mapplic')) return; var instance = (new Mapplic(element)).init(options); // Store plugin object in element's data element.data('mapplic', instance); }); }; })(jQuery);