PageSourceSearch

https://brevix.ru/assets/plugins/mapplic/js/mapplic.js

js brevix.ru collected 2026-10-02 02:07:44 UTC 27,158 bytes, 926 lines download raw bytes

1/**
2 * Mapplic - Custom Interactive Map Plugin by @sekler
3 * http://www.mapplic.com 
4 */
5
6(function($) {
7
8	var Mapplic = function() {
9		var self = this;
10
11		self.o = {
12			source: 'locations.json',
13			height: 420,
14			locations: true,
15			minimap: true,
16			sidebar: true,
17			deeplinking: true,
18			search: true,
19			clearbutton: true,
20			hovertip: true,
21			fullscreen: false,
22			developer: false,
23			animate: true,
24			maxscale: 4
25		};
26
27		self.init = function(el, params) {
28			// Extend options
29			self.o = $.extend(self.o, params);
30
31			self.x = 0;
32			self.y = 0;
33			self.scale = 1;
34
35			self.el = el.addClass('mapplic-element mapplic-loading').height(self.o.height);
36
37			// Process JSON file
38			$.getJSON(self.o.source, function(data) { // Success
39				processData(data);
40				self.el.removeClass('mapplic-loading');
41
42				// Controls
43				addControls();
44
45			}).fail(function() { // Failure: couldn't load JSON file, or it is invalid.
46				console.error('Couldn\'t load map data. (Make sure you are running the script through a server and not just opening the html file with your browser)');
47				alert('Data file missing or invalid!');
48			});
49
50			return self;
51		}
52
53		self.goToLocation = function(location,duration){
54			showLocation(location, duration);
55		}
56		// Tooltip
57		function Tooltip() {
58			this.el = null;
59			this.shift = 6;
60			this.drop = 0;
61
62			this.init = function() {
63				var s = this;
64
65				// Construct
66				this.el = $('<div></div>').addClass('mapplic-tooltip');
67				$('<a></a>').addClass('mapplic-tooltip-close').attr('href', '#').click(function(e) {
68					e.preventDefault();
69					self.deeplinking.clear();
70					s.hide();
71				}).appendTo(this.el);
72				this.image = $('<img>').addClass('mapplic-tooltip-image').hide().appendTo(this.el);
73				this.title = $('<h4></h4>').addClass('mapplic-tooltip-title').appendTo(this.el);
74				this.content = $('<div></div>').addClass('mapplic-tooltip-content').appendTo(this.el);
75				this.desc = $('<div></div>').addClass('mapplic-tooltip-description').appendTo(this.content);
76				this.link = $('<a>More</a>').addClass('mapplic-tooltip-link').attr('href', '#').hide().appendTo(this.el);
77				$('<div></div>').addClass('mapplic-tooltip-triangle').prependTo(this.el);
78
79				// Append
80				self.map.append(this.el);
81			}
82
83			this.set = function(location) {
84				if (location) {
85					if (location.action == 'none') {
86						this.el.stop().fadeOut(300);
87						return;
88					}
89
90					var s = this;
91
92					if (location.image) this.image.attr('src', location.image).show();
93					else this.image.hide();
94
95					if (location.link) this.link.attr('href', location.link).show();
96					else this.link.hide();
97
98					this.title.text(location.title);
99					this.desc.html(location.description);
100
101					// Shift
102					var pinselect = $('.mapplic-pin[data-location="' + location.id + '"]');
103					if (pinselect.length == 0) {
104						this.shift = 6;
105					}
106					else this.shift = pinselect.height() + 6;
107
108					// Loading & positioning
109					$('img', this.desc).load(function() {
110						s.position(location);
111					});
112
113					this.position(location);
114				}
115			}
116
117			this.show = function(location) {
118				if (location) {
119					if (location.action == 'none') {
120						this.el.stop().fadeOut(300);
121						return;
122					}
123
124					var s = this;
125
126					if (location.image) this.image.attr('src', location.image).show();
127					else this.image.hide();
128
129					if (location.link) this.link.attr('href', location.link).show();
130					else this.link.hide();
131
132					this.title.text(location.title);
133					this.desc.html(location.description);
134
135					// Shift
136					var pinselect = $('.mapplic-pin[data-location="' + location.id + '"]');
137					if (pinselect.length == 0) {
138						this.shift = 6;
139					}
140					else this.shift = pinselect.height() + 6;
141
142					// Loading & positioning
143					$('img', this.desc).load(function() {
144						s.position(location);
145					});
146
147					this.position(location);
148				
149					// Making it visible
150					this.el.stop().fadeIn(200).show();
151				}
152			}
153
154			this.position = function(location) {
155				var x = location.x * 100;
156					y = location.y * 100;
157					mt = -this.el.outerHeight() - this.shift,
158					ml = -this.el.outerWidth() / 2;
159				this.el.css({
160					left: x + '%',
161					top: y + '%',
162					marginTop: mt,
163					marginLeft: ml
164				});
165				this.drop = this.el.outerHeight() + this.shift;
166			}
167
168			this.hide = function() {
169				var s = this;
170				
171				this.el.stop().fadeOut(300, function() {
172					s.desc.empty();
173				});
174			}
175		}
176
177		// Deeplinking
178		function Deeplinking() {
179			this.init = function() {
180				// Check hash for location
181				var id = location.hash.slice(1);
182				if (id) {
183					var locationData = getLocationData(id);
184
185					self.tooltip.set(locationData);
186					showLocation(id, 0);
187					self.tooltip.show(locationData);
188				}
189				else zoomTo(0.5, 0.5, 1, 0);
190
191				// Hashchange
192				$(window).on('hashchange', function() {
193					var id = location.hash.slice(1);
194
195					if (id) {
196						var locationData = getLocationData(id);
197
198						self.tooltip.set(locationData);
199						showLocation(id, 800);
200						self.tooltip.show(locationData);
201					}
202				});
203			}
204
205			this.clear = function() {
206				// if IE 6-8, else normal browsers
207				if (history.pushState) history.pushState('', document.title, window.location.pathname);
208				else window.location.hash = '';
209			}
210		}
211
212		// HoverTooltip
213		function HoverTooltip() {
214			this.el = null;
215			this.shift = 6;
216
217			this.init = function() {
218				var s = this;
219
220				// Construct
221				this.el = $('<div></div>').addClass('mapplic-tooltip mapplic-hovertip');
222				this.title = $('<h4></h4>').addClass('mapplic-tooltip-title').appendTo(this.el);
223				$('<div></div>').addClass('mapplic-tooltip-triangle').appendTo(this.el);
224
225				// Events
226				$(self.map).on('mouseover', '.mapplic-layer a', function() {
227					var data = '';
228					if ($(this).hasClass('mapplic-pin')) {
229						data = $(this).data('location');
230						s.shift = $(this).height() + 6;
231					}
232					else {
233						data = $(this).attr('xlink:href').slice(1);
234						s.shift = 6;
235					}
236
237					var location = getLocationData(data);
238					if (location) s.show(location);
239				}).on('mouseout', function() {
240					s.hide();
241				});
242
243				self.map.append(this.el);
244			}
245
246			this.show = function(location) {
247				this.title.text(location.title);
248
249				var x = location.x * 100,
250					y = location.y * 100,
251					mt = -this.el.outerHeight() - this.shift,
252					ml = -this.el.outerWidth() / 2;
253				this.el.css({
254					left: x + '%',
255					top: y + '%',
256					marginTop: mt,
257					marginLeft: ml
258				});
259
260				this.el.stop().fadeIn(100);
261			}
262
263			this.hide = function() {
264				this.el.stop().fadeOut(200);
265			}
266		}
267
268		// Minimap
269		function Minimap() {
270			this.el = null;
271
272			this.init = function() {
273				this.el = $('<div></div>').addClass('mapplic-minimap').appendTo(self.container);
274				this.el.css('height', this.el.width() * self.hw_ratio);
275				this.el.click(function(e) {
276					e.preventDefault();
277
278					var x = (e.pageX - $(this).offset().left) / $(this).width(),
279						y = (e.pageY - $(this).offset().top) / $(this).height();
280
281					zoomTo(x, y, self.scale / self.fitscale, 100);
282				});
283			}
284
285			this.addLayer = function(data) {
286				var layer = $('<div></div>').addClass('mapplic-minimap-layer').addClass(data.id).appendTo(this.el);
287				$('<img>').attr('src', data.minimap).addClass('mapplic-minimap-background').appendTo(layer);
288				$('<div></div>').addClass('mapplic-minimap-overlay').appendTo(layer);
289				$('<img>').attr('src', data.minimap).addClass('mapplic-minimap-active').appendTo(layer);
290			}
291
292			this.show = function(target) {
293				$('.mapplic-minimap-layer:visible', this.el).hide();
294				$('.mapplic-minimap-layer.' + target, this.el).show();
295			}
296
297			this.update = function(x, y) {
298				var active = $('.mapplic-minimap-active', this.el);
299
300				if (x === undefined) x = self.x;
301				if (y === undefined) y = self.y;
302
303				var width = Math.round(self.container.width() / self.contentWidth / self.scale * this.el.width()),
304					height = Math.round(self.container.height() / self.contentHeight / self.scale * this.el.height()),
305					top = Math.round(-y / self.contentHeight / self.scale * this.el.height()),
306					left = Math.round(-x / self.contentWidth / self.scale * this.el.width()),
307					right = left + width,
308					bottom = top + height;
309
310				active.css('clip', 'rect(' + top + 'px, ' + right + 'px, ' + bottom + 'px, ' + left + 'px)');
311			}
312		}
313
314		// Sidebar
315		function Sidebar() {
316			this.el = null;
317			this.list = null;
318
319			this.init = function() {
320				var s = this;
321
322				this.el = $('<div></div>').addClass('mapplic-sidebar').appendTo(self.el);
323
324				if (self.o.search) {
325					var form = $('<form></form>').addClass('mapplic-search-form').submit(function() {
326						return false;
327					}).appendTo(this.el);
328					self.clear = $('<button></button>').addClass('mapplic-search-clear').click(function() {
329						input.val('');
330						input.keyup();
331					}).appendTo(form);
332					var input = $('<input>').attr({'type': 'text', 'spellcheck': 'false', 'placeholder': 'Search for location...'}).addClass('mapplic-search-input').keyup(function() {
333						var keyword = $(this).val();
334						s.search(keyword);
335					}).prependTo(form);
336				}
337
338				var listContainer = $('<div></div>').addClass('mapplic-list-container').appendTo(this.el);
339				this.list = $('<ol></ol>').addClass('mapplic-list').appendTo(listContainer);
340				this.notfound = $('<p></p>').addClass('mapplic-not-found').text('Nothing found. Please try a different search.').appendTo(listContainer);
341
342				if (!self.o.search) listContainer.css('padding-top', '0');
343			}
344
345			this.addCategories = function(categories) {
346				var list = this.list;
347
348				$.each(categories, function(index, category) {
349					var item = $('<li></li>').addClass('mapplic-list-category').addClass(category.id);
350					var ol = $('<ol></ol>').css('border-color', category.color).appendTo(item);
351					if (category.show == 'false') ol.hide();
352					var link = $('<a></a>').attr('href', '#').attr('title', category.title).css('background-color', category.color).text(category.title).click(function(e) {
353						ol.slideToggle(200);
354						return false;
355					}).prependTo(item);
356					if (category.icon) $('<img>').attr('src', category.icon).addClass('mapplic-list-thumbnail').prependTo(link);
357					$('<span></span>').text('0').addClass('mapplic-list-count').prependTo(link);
358					list.append(item);
359				});
360			}
361
362			this.addLocation = function(data) {
363				var item = $('<li></li>').addClass('mapplic-list-location').addClass('mapplic-list-shown');
364				var link = $('<a></a>').attr('href', '#' + data.id).appendTo(item);
365				if (data.thumbnail) $('<img>').attr('src', data.thumbnail).addClass('mapplic-list-thumbnail').appendTo(link);
366				$('<h4></h4>').text(data.title).appendTo(link)
367				$('<span></span>').html(data.about).appendTo(link);
368				var category = $('.mapplic-list-category.' + data.category);
369
370				if (category.length) $('ol', category).append(item);
371				else this.list.append(item);
372
373				// Count
374				$('.mapplic-list-count', category).text($('.mapplic-list-shown', category).length);
375			}
376
377			this.search = function(keyword) {
378				if (keyword) self.clear.fadeIn(100);
379				else self.clear.fadeOut(100);
380
381				$('.mapplic-list li', self.el).each(function() {
382					if ($(this).text().search(new RegExp(keyword, "i")) < 0) {
383						$(this).removeClass('mapplic-list-shown');
384						$(this).slideUp(200);
385					} else {
386						$(this).addClass('mapplic-list-shown');
387						$(this).show();
388					}
389				});
390
391				$('.mapplic-list > li', self.el).each(function() {
392					var count = $('.mapplic-list-shown', this).length;
393					$('.mapplic-list-count', this).text(count);
394				});
395
396				// Show not-found text
397				if ($('.mapplic-list > li.mapplic-list-shown').length > 0) this.notfound.fadeOut(200);
398				else this.notfound.fadeIn(200);
399			}
400		}
401
402		// Developer tools
403		function DevTools() {
404			this.el = null;
405
406			this.init = function() {
407				this.el = $('<div></div>').addClass('mapplic-coordinates').appendTo(self.container);
408				this.el.append('x: ');
409				$('<code></code>').addClass('mapplic-coordinates-x').appendTo(this.el);
410				this.el.append(' y: ');
411				$('<code></code>').addClass('mapplic-coordinates-y').appendTo(this.el);
412
413				$('.mapplic-layer', self.map).on('mousemove', function(e) {
414					var x = (e.pageX - self.map.offset().left) / self.map.width(),
415						y = (e.pageY - self.map.offset().top) / self.map.height();
416					$('.mapplic-coordinates-x').text(parseFloat(x).toFixed(4));
417					$('.mapplic-coordinates-y').text(parseFloat(y).toFixed(4));
418				});
419			}
420		}
421
422		// Clear Button
423		function ClearButton() {
424			this.el = null;
425			
426			this.init = function() {
427				this.el = $('<a></a>').attr('href', '#').addClass('mapplic-clear-button').click(function(e) {
428					e.preventDefault();
429					self.deeplinking.clear();
430					self.tooltip.hide();
431					zoomTo(0.5, 0.5, 1);
432				}).appendTo(self.container);
433			}
434		}
435
436		// Full Screen
437		function FullScreen() {
438			this.el = null;
439
440			this.init = function() {
441				var s = this;
442				this.element = self.el[0];
443
444				$('<a></a>').attr('href', '#').attr('href', '#').addClass('mapplic-fullscreen-button').click(function(e) {
445					e.preventDefault();
446
447					if (s.isFull()) s.exitFull();
448					else s.goFull();
449
450				}).appendTo(self.container);
451			}
452
453			this.goFull = function() {
454				if (this.element.requestFullscreen) this.element.requestFullscreen();
455				else if(this.element.mozRequestFullScreen) this.element.mozRequestFullScreen();
456				else if(this.element.webkitRequestFullscreen) this.element.webkitRequestFullscreen();
457				else if(this.element.msRequestFullscreen) this.element.msRequestFullscreen();
458			}
459
460			this.exitFull = function() {
461				if (document.exitFullscreen) document.exitFullscreen();
462				else if(document.mozCancelFullScreen) document.mozCancelFullScreen();
463				else if(document.webkitExitFullscreen) document.webkitExitFullscreen();
464			}
465
466			this.isFull = function() {
467				if (window.innerHeight == screen.height) {
468					return true;
469				} else {
470					return false;
471				}
472			}
473		}
474
475		// Functions
476		var processData = function(data) {
477			self.data = data;
478			var nrlevels = 0;
479			var shownLevel;
480
481			self.container = $('<div></div>').addClass('mapplic-container').appendTo(self.el);
482			self.map = $('<div></div>').addClass('mapplic-map').appendTo(self.container);
483
484			self.levelselect = $('<select></select>').addClass('mapplic-levels-select');
485
486			if (!self.o.sidebar) self.container.css('width', '100%');
487
488			self.contentWidth = data.mapwidth;
489			self.contentHeight = data.mapheight;
490
491			self.hw_ratio = data.mapheight / data.mapwidth;
492			if (data.mapheight / self.container.height() > data.mapwidth / self.container.width()) {
493				self.min_width = self.container.width();
494				self.min_height = self.container.width() * self.hw_ratio;
495			}
496			else {
497				self.min_height = self.container.height();
498				self.min_width = self.container.height() / self.hw_ratio;
499			}
500
501			self.map.css({
502				'width': data.mapwidth,
503				'height': data.mapheight
504			});
505
506			// Create minimap
507			if (self.o.minimap) {
508				self.minimap = new Minimap();
509				self.minimap.init();
510			}
511
512			// Create sidebar
513			if (self.o.sidebar) {
514				self.sidebar = new Sidebar();
515				self.sidebar.init();
516				self.sidebar.addCategories(data.categories);
517			}
518
519			// Iterate through levels
520			if (data.levels) {
521				$.each(data.levels, function(index, value) {
522					var source = value.map;
523					var extension = source.substr((source.lastIndexOf('.') + 1)).toLowerCase();
524
525					// Create new map layer
526					var layer = $('<div></div>').addClass('mapplic-layer').addClass(value.id).hide().appendTo(self.map);
527					switch (extension) {
528
529						// Image formats
530						case 'jpg': case 'jpeg': case 'png': case 'gif':
531							$('<img>').attr('src', source).addClass('mapplic-map-image').appendTo(layer);
532							break;
533
534						// Vector format
535						case 'svg':
536							$('<div></div>').addClass('mapplic-map-image').load(source).appendTo(layer);
537							break;
538
539						// Other 
540						default:
541							alert('File type ' + extension + ' is not supported!');
542					}
543
544					// Create new minimap layer
545					if (self.minimap) self.minimap.addLayer(value);
546
547					// Build layer control
548					self.levelselect.prepend($('<option></option>').attr('value', value.id).text(value.title));
549
550					if (!shownLevel || value.show) {
551						shownLevel = value.id;
552					}
553					
554					/* Iterate through locations */
555					$.each(value.locations, function(index, value) {
556						var top = value.y * 100;
557						var left = value.x * 100;
558
559						if (value.pin != 'hidden') {
560							if (self.o.locations) {
561								var target = '#' + value.id;
562								if (value.action == 'redirect') target = value.link;
563
564								var pin = $('<a></a>').attr('href', target).addClass('mapplic-pin').css({'top': top + '%', 'left': left + '%'}).appendTo(layer);
565								pin.attr('data-location', value.id);
566								pin.addClass(value.pin);
567							}
568						}
569
570						if (self.sidebar) self.sidebar.addLocation(value);
571					});
572
573					nrlevels++;
574				});
575			}
576
577			// Pin animation
578			if (self.o.animate) {
579				$('.mapplic-pin').css('opacity', '0');
580				window.setTimeout(animateNext, 200);
581			}
582
583			function animateNext() {
584				var select = $('.mapplic-pin:not(.mapplic-animate):visible');
585
586				//console.log('enter');
587
588				if (select.length > 0) {
589					select.first().addClass('mapplic-animate');
590					window.setTimeout(animateNext, 200);
591				}
592				else {
593					$('.mapplic-animate').removeClass('mapplic-animate');
594					$('.mapplic-pin').css('opacity', '1');
595				}
596			}
597
598			// COMPONENTS
599
600			// Hover Tooltip
601			if (self.o.hovertip) self.hovertip = new HoverTooltip().init();
602
603			// Tooltip
604			self.tooltip = new Tooltip();
605			self.tooltip.init();
606
607			// Developer tools
608			if (self.o.developer) self.devtools = new DevTools().init();
609
610			// Clear button
611			if (self.o.clearbutton) self.clearbutton = new ClearButton().init();
612
613			// Fullscreen
614			if (self.o.fullscreen) self.fullscreen = new FullScreen().init();
615
616			// Levels
617			if (nrlevels > 1) {
618				self.levels = $('<div></div>').addClass('mapplic-levels');
619				var up = $('<a href="#"></a>').addClass('mapplic-levels-up').appendTo(self.levels);
620				self.levelselect.appendTo(self.levels);
621				var down = $('<a href="#"></a>').addClass('mapplic-levels-down').appendTo(self.levels);
622				self.container.append(self.levels);
623			
624				self.levelselect.change(function() {
625					var value = $(this).val();
626					level(value);
627				});
628			
629				up.click(function(e) {
630					e.preventDefault();
631					if (!$(this).hasClass('disabled')) level('+');
632				});
633
634				down.click(function(e) {
635					e.preventDefault();
636					if (!$(this).hasClass('disabled')) level('-');
637				});
638			}
639			level(shownLevel);
640
641			// Browser resize
642			$(window).resize(function() {
643				var wr = self.container.width() / self.contentWidth,
644					hr = self.container.height() / self.contentHeight;
645
646				if (wr > hr) self.fitscale = wr;
647				else self.fitscale = hr;
648
649				self.scale = normalizeScale(self.scale);
650				self.x = normalizeX(self.x);
651				self.y = normalizeY(self.y);
652
653				moveTo(self.x, self.y, self.scale, 100);
654			}).resize();
655
656			// Deeplinking
657			if (self.o.deeplinking) {
658				self.deeplinking = new Deeplinking();
659				self.deeplinking.init();
660			}
661		}
662
663		var addControls = function() {
664			var map = self.map,
665				mapbody = $('.mapplic-map-image', self.map);
666
667			document.ondragstart = function() { return false; } // IE drag fix
668
669			// Drag & drop
670			mapbody.on('mousedown', function(event) {
671				map.stop();
672
673				map.data('mouseX', event.pageX);
674				map.data('mouseY', event.pageY);
675				map.data('lastX', self.x);
676				map.data('lastY', self.y);
677
678				map.addClass('mapplic-dragging');
679
680				self.map.on('mousemove', function(event) {
681					var x = event.pageX - map.data('mouseX') + self.x;
682						y = event.pageY - map.data('mouseY') + self.y;
683
684					x = normalizeX(x);
685					y = normalizeY(y);
686
687					moveTo(x, y);
688					map.data('lastX', x);
689					map.data('lastY', y);
690				});
691			
692				$(document).on('mouseup', function(event) {
693					self.x = map.data('lastX');
694					self.y = map.data('lastY');
695
696					self.map.off('mousemove');
697					$(document).off('mouseup');
698
699					map.removeClass('mapplic-dragging');
700				});
701			});
702
703			// Double click
704			$(document).on('dblclick', '.mapplic-map-image', function(event) {
705				var mapPos = self.map.offset();
706				var x = (event.pageX - mapPos.left) / self.map.width();
707				var y = (event.pageY - mapPos.top) / self.map.height();
708				var z = self.map.width() / self.min_width * 2;
709
710				zoomTo(x, y, z, 600);
711			});
712
713			// Mousewheel
714			$('.mapplic-layer', this.el).bind('mousewheel DOMMouseScroll', function(event, delta) {
715				event.preventDefault();
716
717				var scale = self.scale;
718				self.scale = normalizeScale(scale + scale * delta/5);
719
720				self.x = normalizeX(self.x - (event.pageX - self.container.offset().left - self.x) * (self.scale/scale - 1));
721				self.y = normalizeY(self.y - (event.pageY - self.container.offset().top - self.y) * (self.scale/scale - 1));
722
723				moveTo(self.x, self.y, self.scale, 100);
724			});
725
726			// Touch support
727			if (!('ontouchstart' in window || 'onmsgesturechange' in window)) return true;
728
729			mapbody.on('touchstart', function(e) {
730				var orig = e.originalEvent,
731					pos = map.position();
732
733				map.data('touchY', orig.changedTouches[0].pageY - pos.top);
734				map.data('touchX', orig.changedTouches[0].pageX - pos.left);
735
736				mapbody.on('touchmove', function(e) {
737					e.preventDefault();
738					var orig = e.originalEvent;
739					var touches = orig.touches.length;
740
741					if (touches == 1) {
742						self.x = normalizeX(orig.changedTouches[0].pageX - map.data('touchX'));
743						self.y = normalizeY(orig.changedTouches[0].pageY - map.data('touchY'));
744
745						moveTo(self.x, self.y, self.scale, 100);
746					}
747					else {
748						mapbody.off('touchmove');
749					}
750				});
751
752				mapbody.on('touchend', function(e) {
753					mapbody.off('touchmove touchend');
754				});
755			});
756			
757			// Pinch zoom
758			var mapPinch = Hammer(self.map[0], {
759				transform_always_block: true,
760				drag_block_horizontal: true,
761				drag_block_vertical: true
762			});
763
764			var scale=1, last_scale;
765
766			mapPinch.on('touch transform', function(ev) {
767				switch(ev.type) {
768					case 'touch':
769						last_scale = scale;
770						break;
771
772					case 'transform':
773						var center = ev.gesture.center;
774						scale = Math.max(1, Math.min(last_scale * ev.gesture.scale, 10));
775
776						var oldscale = self.scale;
777						self.scale = normalizeScale(scale * self.fitscale);
778
779						self.x = normalizeX(self.x - (center.pageX - self.container.offset().left - self.x) * (self.scale/oldscale - 1));
780						self.y = normalizeY(self.y - (center.pageY - self.container.offset().top - self.y) * (self.scale/oldscale - 1));
781
782						moveTo(self.x, self.y, self.scale, 200);
783
784						break;
785				}
786			});
787		}
788
789		var level = function(target) {
790			switch (target) {
791				case '+':
792					target = $('option:selected', self.levelselect).removeAttr('selected').prev().prop('selected', 'selected').val();
793					break;
794				case '-':
795					target = $('option:selected', self.levelselect).removeAttr('selected').next().prop('selected', 'selected').val();
796					break;
797				default:
798					$('option[value="' + target + '"]', self.levelselect).prop('selected', 'selected');
799			}
800
801			var layer = $('.mapplic-layer.' + target, self.map);
802
803			// Target layer is active
804			if (layer.is(':visible')) return;
805
806			// Hide Tooltip
807			self.tooltip.hide();
808
809			// Show target layer
810			$('.mapplic-layer:visible', self.map).hide();
811			layer.show();
812
813			// Show target minimap layer
814			if (self.minimap) self.minimap.show(target);
815
816			// Update control
817			var index = self.levelselect.get(0).selectedIndex,
818				up = $('.mapplic-levels-up', self.levels),
819				down = $('.mapplic-levels-down', self.levels);
820
821			up.removeClass('disabled');
822			down.removeClass('disabled');
823			if (index == 0) {
824				up.addClass('disabled');
825			}
826			else if (index == self.levelselect.get(0).length - 1) {
827				down.addClass('disabled');
828			}
829		}
830
831		var getLocationData = function(id) {
832			var data = null;
833			$.each(self.data.levels, function(index, layer) {
834				$.each(layer.locations, function(index, value) {
835					if (value.id == id) {
836						data = value;
837					}
838				});
839			});
840			return data;
841		}
842
843		var showLocation = function(id, duration) {
844			$.each(self.data.levels, function(index, layer) {
845				$.each(layer.locations, function(index, value) {
846					if (value.id == id) {
847						var zoom = typeof value.zoom !== 'undefined' ? value.zoom : 4,		
848							drop = self.tooltip.drop / self.contentHeight / zoom;
849
850						level(layer.id);
851
852						zoomTo(value.x, parseFloat(value.y) - drop, zoom, duration, 'easeInOutCubic');
853					}
854				});
855			});
856		};
857
858		var normalizeX = function(x) {
859			var minX = self.container.width() - self.contentWidth * self.scale;
860
861			if (x > 0) x = 0;
862			else if (x < minX) x = minX;
863
864			return x;
865		}
866
867		var normalizeY = function(y) {
868			var minY = self.container.height() - self.contentHeight * self.scale;
869
870			if (y >= 0) y = 0;
871			else if (y < minY) y = minY;
872
873			return y;
874		}
875
876		var normalizeScale = function(scale) {
877			if (scale < self.fitscale) scale = self.fitscale;
878			else if (scale > self.o.maxscale) scale = self.o.maxscale;
879
880			return scale;
881		}
882
883		var zoomTo = function(x, y, s, duration, easing) {
884			duration = typeof duration !== 'undefined' ? duration : 400;
885
886			self.scale = normalizeScale(self.fitscale * s);
887			var scale = self.contentWidth * self.scale;
888
889			self.x = normalizeX(self.container.width() * 0.5 - self.scale * self.contentWidth * x);
890			self.y = normalizeY(self.container.height() * 0.5 - self.scale * self.contentHeight * y);
891
892			moveTo(self.x, self.y, self.scale, duration, easing);
893		}
894
895		var moveTo = function(x, y, scale, d, easing) {
896			if (scale !== undefined) {
897				self.map.stop().animate({
898					'left': x,
899					'top': y,
900					'width': self.contentWidth * scale,
901					'height': self.contentHeight * scale
902				}, d, easing);
903			}
904			else {
905				self.map.css({
906					'left': x,
907					'top': y
908				});
909			}
910			if (self.minimap) self.minimap.update(x, y);
911		}
912	};
913
914	//  Create a jQuery plugin
915	$.fn.mapplic = function(params) {
916		var len = this.length;
917
918		return this.each(function(index) {
919			var me = $(this),
920				key = 'mapplic' + (len > 1 ? '-' + ++index : ''),
921				instance = (new Mapplic).init(me, params);
922
923			me.data(key, instance).data('key', key);
924		});
925	};
926})(jQuery);

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.