Subversion-Projekte lars-tiefland.cienc

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
5 lars 1
/*
2
 * Mapplic - Custom Interactive Map Plugin by @sekler
3
 * Version 3.1
4
 * http://www.mapplic.com
5
 */
6
 
7
(function($) {
8
 
9
	var Mapplic = function() {
10
		var self = this;
11
 
12
		self.o = {
13
			source: 'locations.json',
14
			selector: '[id^=landmarks] > *',
15
			landmark: null,
16
			mapfill: false,
17
			height: 420,
18
			markers: true,
19
			minimap: true,
20
			sidebar: true,
21
			search: true,
22
			deeplinking: true,
23
			clearbutton: true,
24
			zoombuttons: true,
25
			hovertip: true,
26
			smartip: true,
27
			fullscreen: false,
28
			developer: false,
29
			maxscale: 4,
30
			skin: '',
31
			zoom: true
32
		};
33
 
34
		self.init = function(el, params) {
35
			// Extend options
36
			self.o = $.extend(self.o, params);
37
 
38
			self.x = 0;
39
			self.y = 0;
40
			self.scale = 1;
41
 
42
			self.el = el.addClass('mapplic-element mapplic-loading').addClass(self.o.skin).height(self.o.height);
43
 
44
			// Disable modules when landmark mode is active
45
			if (self.o.landmark) {
46
				self.o.sidebar = false;
47
				self.o.zoombuttons = false;
48
				self.o.deeplinking = false;
49
			}
50
 
51
			if (typeof self.o.source === 'string') {
52
				// Loading .json file with AJAX
53
				$.getJSON(self.o.source, function(data) { // Success
54
					processData(data);
55
					self.el.removeClass('mapplic-loading');
56
 
57
				}).fail(function() { // Failure: couldn't load JSON file, or it is invalid.
58
					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)');
59
					self.el.removeClass('mapplic-loading').addClass('mapplic-error');
60
					alert('Data file missing or invalid!');
61
				});
62
			}
63
			else {
64
				// Inline json object
65
				processData(self.o.source);
66
				self.el.removeClass('mapplic-loading');
67
			}
68
 
69
			return self;
70
		}
71
 
72
		// Tooltip
73
		function Tooltip() {
74
			this.el = null;
75
			this.shift = 6;
76
			this.drop = 0;
77
			this.location = null;
78
 
79
			this.init = function() {
80
				var s = this;
81
 
82
				// Construct
83
				this.el = $('<div></div>').addClass('mapplic-tooltip');
84
				this.close = $('<a></a>').addClass('mapplic-tooltip-close').attr('href', '#').appendTo(this.el);
85
				this.close.on('click touchend', function(e) {
86
					e.preventDefault();
87
					$('.mapplic-active', self.el).attr('class', 'mapplic-clickable');
88
					if (self.deeplinking) self.deeplinking.clear();
89
					if (!self.o.zoom) zoomTo(0.5, 0.5, 1, 600, 'easeInOutCubic');
90
					s.hide();
91
				});
92
				this.image = $('<img>').addClass('mapplic-tooltip-image').hide().appendTo(this.el);
93
				this.title = $('<h4></h4>').addClass('mapplic-tooltip-title').appendTo(this.el);
94
				this.content = $('<div></div>').addClass('mapplic-tooltip-content').appendTo(this.el);
95
				this.desc = $('<div></div>').addClass('mapplic-tooltip-description').appendTo(this.content);
96
				this.link = $('<a>More</a>').addClass('mapplic-tooltip-link').attr('href', '#').hide().appendTo(this.el);
97
				this.triangle = $('<div></div>').addClass('mapplic-tooltip-triangle').prependTo(this.el);
98
 
99
				// Append
100
				self.map.append(this.el);
101
			}
102
 
103
			this.set = function(location) {
104
				if (location) {
105
					var s = this;
106
 
107
					if (location.image) this.image.attr('src', location.image).show();
108
					else this.image.hide();
109
 
110
					if (location.link) this.link.attr('href', location.link).show();
111
					else this.link.hide();
112
 
113
					this.title.text(location.title);
114
					this.desc.html(location.description);
115
					this.content[0].scrollTop = 0;
116
 
117
					this.position(location);
118
				}
119
			}
120
 
121
			this.show = function(location) {
122
				if (location) {
123
					if (location.action == 'none') {
124
						this.el.stop().fadeOut(300);
125
						return;
126
					}
127
 
128
					var s = this;
129
 
130
					this.location = location;
131
					self.hovertip.hide();
132
 
133
					if (location.image) this.image.attr('src', location.image).show();
134
					else this.image.hide();
135
 
136
					if (location.link) this.link.attr('href', location.link).show();
137
					else this.link.hide();
138
 
139
					this.title.text(location.title);
140
					this.desc.html(location.description);
141
 
142
					// Shift
143
					var pinselect = $('.mapplic-pin[data-location="' + location.id + '"]');
144
					if (pinselect.length == 0) {
145
						this.shift = 20;
146
					}
147
					else this.shift = pinselect.height() + 10;
148
 
149
					// Loading & positioning
150
					$('img', this.el).load(function() {
151
						s.position();
152
					});
153
					this.position();
154
 
155
					// Making it visible
156
					this.el.stop().show();
157
				}
158
			}
159
 
160
			this.position = function() {
161
				if (this.location) {
162
					var cx = self.map.offset().left + self.map.width() * this.location.x - self.container.offset().left,
163
						cy = self.map.offset().top + self.map.height() * this.location.y - self.container.offset().top;
164
 
165
					var x = this.location.x * 100,
166
						y = this.location.y * 100,
167
						mt = -this.el.outerHeight() - this.shift,
168
						ml = -this.el.outerWidth() / 2;
169
 
170
					if (self.o.smartip) {
171
						var verticalPos = 0.5;
172
 
173
						// Top check
174
						if (Math.abs(mt) > cy) {
175
							mt = 8 + 2;
176
							this.el.addClass('mapplic-bottom');
177
						}
178
						else this.el.removeClass('mapplic-bottom');
179
 
180
						// Left-right check
181
						if (this.el.outerWidth()/2 > cx)
182
							verticalPos = 0.5 - (this.el.outerWidth()/2 - cx)/this.el.outerWidth();
183
						else if ((self.container.width() - cx - this.el.outerWidth()/2) < 0)
184
							verticalPos = 0.5 + (cx + this.el.outerWidth()/2 - self.container.width())/this.el.outerWidth();
185
 
186
						verticalPos = Math.max(0, Math.min(1, verticalPos));
187
						ml = -this.el.outerWidth() * verticalPos;
188
						this.triangle.css('left', Math.max(5, Math.min(95, verticalPos * 100)) + '%');
189
					}
190
 
191
					this.el.css({
192
						left: x + '%',
193
						top: y + '%',
194
						marginTop: mt,
195
						marginLeft: ml
196
					});
197
					this.drop = /*this.el.outerHeight()*/ 240 + this.shift;
198
				}
199
			}
200
 
201
			this.hide = function() {
202
				var s = this;
203
 
204
				this.location = null;
205
 
206
				this.el.stop().fadeOut(300, function() {
207
					s.desc.empty();
208
				});
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
				this.triangle = $('<div></div>').addClass('mapplic-tooltip-triangle').appendTo(this.el);
224
 
225
				// Events
226
				// pins + old svg
227
				$(self.map).on('mouseover', '.mapplic-layer a', function() {
228
					var id = '';
229
					if ($(this).hasClass('mapplic-pin')) {
230
						id = $(this).data('location');
231
						s.shift = $(this).height() + 10;
232
					}
233
					else {
234
						id = $(this).attr('xlink:href').slice(1);
235
						s.shift = 20;
236
					}
237
 
238
					var location = getLocationData(id);
239
					if (location) s.show(location);
240
				}).on('mouseout', function() {
241
					s.hide();
242
				});
243
 
244
				// new svg
245
				if (self.o.selector) {
246
					$(self.map).on('mouseover', self.o.selector, function() {
247
						var location = getLocationData($(this).attr('id'));
248
						s.shift = 20;
249
						if (location) s.show(location);
250
					}).on('mouseout', function() {
251
						s.hide();
252
					});
253
				}
254
 
255
				self.map.append(this.el);
256
			}
257
 
258
			this.show = function(location) {
259
				if (self.tooltip.location != location) {
260
					this.title.text(location.title);
261
 
262
					this.position(location);
263
 
264
					this.el.stop().fadeIn(100);
265
				}
266
			}
267
 
268
			this.position = function(location) {
269
				var cx = self.map.offset().left + self.map.width() * location.x - self.container.offset().left,
270
					cy = self.map.offset().top + self.map.height() * location.y - self.container.offset().top;
271
 
272
				var x = location.x * 100,
273
					y = location.y * 100,
274
					mt = -this.el.outerHeight() - this.shift,
275
					ml = 0;
276
 
277
				var verticalPos = 0.5;
278
 
279
				// Top check
280
				if (Math.abs(mt) > cy) {
281
					mt = 8 + 2;
282
					this.el.addClass('mapplic-bottom');
283
				}
284
				else this.el.removeClass('mapplic-bottom');
285
 
286
				// Left-right check
287
				if (this.el.outerWidth()/2 > cx)
288
					verticalPos = 0.5 - (this.el.outerWidth()/2 - cx)/this.el.outerWidth();
289
				else if ((self.container.width() - cx - this.el.outerWidth()/2) < 0)
290
					verticalPos = 0.5 + (cx + this.el.outerWidth()/2 - self.container.width())/this.el.outerWidth();
291
 
292
				ml = -this.el.outerWidth() * verticalPos;
293
				this.triangle.css('left', Math.max(10, Math.min(90, verticalPos * 100)) + '%');
294
				this.el.css({
295
					left: x + '%',
296
					top: y + '%',
297
					marginTop: mt,
298
					marginLeft: ml
299
				});
300
			}
301
 
302
			this.hide = function() {
303
				this.el.stop().fadeOut(200);
304
			}
305
		}
306
 
307
		// Deeplinking
308
		function Deeplinking() {
309
			this.param = 'location';
310
 
311
			this.init = function() {
312
				var s = this;
313
				this.check(0);
314
 
315
				window.onpopstate = function(e) {
316
					if (e.state) {
317
						s.check(600);
318
					}
319
					return false;
320
				}
321
			}
322
 
323
			this.check = function(ease) {
324
				var id = this.getUrlParam(this.param);
325
				showLocation(id, ease, true);
326
			}
327
 
328
			this.getUrlParam = function(name) {
329
				name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
330
				var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
331
					results = regex.exec(location.search);
332
				return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
333
			}
334
 
335
			this.update = function(id) {
336
				var url = window.location.protocol + "//" + window.location.host + window.location.pathname + '?' + this.param + '=' + id;
337
				window.history.pushState({path: url}, '', url);
338
			}
339
 
340
			// Clear
341
			this.clear = function() {
342
				history.pushState('', document.title, window.location.pathname);
343
			}
344
		}
345
 
346
		// Old hash deeplinking method for old browsers
347
		function DeeplinkingHash() {
348
			this.param = 'location';
349
 
350
			this.init = function() {
351
				var s = this;
352
				this.check(0);
353
 
354
				$(window).on('hashchange', function() {
355
					s.check(600);
356
				});
357
			}
358
 
359
			this.check = function(ease) {
360
				var id = location.hash.slice(this.param.length + 2);
361
				showLocation(id, ease, true);
362
			}
363
 
364
			this.update = function(id) {
365
				window.location.hash = this.param + '-' + id;
366
			}
367
 
368
			this.clear = function() {
369
				window.location.hash = this.param;
370
			}
371
		}
372
 
373
		// Minimap
374
		function Minimap() {
375
			this.el = null;
376
			this.opacity = null;
377
 
378
			this.init = function() {
379
				this.el = $('<div></div>').addClass('mapplic-minimap').appendTo(self.container);
380
				this.el.css('height', this.el.width() * self.hw_ratio);
381
				this.el.click(function(e) {
382
					e.preventDefault();
383
 
384
					var x = (e.pageX - $(this).offset().left) / $(this).width(),
385
						y = (e.pageY - $(this).offset().top) / $(this).height();
386
 
387
					zoomTo(x, y, self.scale / self.fitscale, 100);
388
				});
389
			}
390
 
391
			this.addLayer = function(data) {
392
				var layer = $('<div></div>').addClass('mapplic-minimap-layer').addClass(data.id).appendTo(this.el);
393
				$('<img>').attr('src', data.minimap).addClass('mapplic-minimap-background').appendTo(layer);
394
				$('<div></div>').addClass('mapplic-minimap-overlay').appendTo(layer);
395
				$('<img>').attr('src', data.minimap).addClass('mapplic-minimap-active').appendTo(layer);
396
			}
397
 
398
			this.show = function(target) {
399
				$('.mapplic-minimap-layer', this.el).hide();
400
				$('.mapplic-minimap-layer.' + target, this.el).show();
401
			}
402
 
403
			this.update = function(x, y) {
404
				var active = $('.mapplic-minimap-active', this.el);
405
 
406
				if (x === undefined) x = self.x;
407
				if (y === undefined) y = self.y;
408
 
409
				var width = Math.round(self.container.width() / self.contentWidth / self.scale * this.el.width()),
410
					height = Math.round(self.container.height() / self.contentHeight / self.scale * this.el.height()),
411
					top = Math.round(-y / self.contentHeight / self.scale * this.el.height()),
412
					left = Math.round(-x / self.contentWidth / self.scale * this.el.width()),
413
					right = left + width,
414
					bottom = top + height;
415
 
416
				active.each(function() {
417
					$(this)[0].style.clip = 'rect(' + top + 'px, ' + right + 'px, ' + bottom + 'px, ' + left + 'px)';
418
				});
419
 
420
 
421
				var s = this;
422
				this.el.show();
423
				this.el.css('opacity', 1.0);
424
				clearTimeout(this.opacity);
425
				this.opacity = setTimeout(function() {
426
					s.el.css('opacity', 0);
427
					setTimeout(function() { s.el.hide(); }, 600);
428
				}, 2000);
429
			}
430
		}
431
 
432
		// Sidebar
433
		function Sidebar() {
434
			this.el = null;
435
			this.list = null;
436
 
437
			this.init = function() {
438
				var s = this;
439
 
440
				this.el = $('<div></div>').addClass('mapplic-sidebar').appendTo(self.el);
441
 
442
				if (self.o.search) {
443
					var form = $('<form></form>').addClass('mapplic-search-form').submit(function() {
444
						return false;
445
					}).appendTo(this.el);
446
					self.clear = $('<button></button>').addClass('mapplic-search-clear').click(function() {
447
						input.val('');
448
						input.keyup();
449
					}).appendTo(form);
450
					var input = $('<input>').attr({'type': 'text', 'spellcheck': 'false', 'placeholder': 'Search...'}).addClass('mapplic-search-input').keyup(function() {
451
						var keyword = $(this).val();
452
						s.search(keyword);
453
					}).prependTo(form);
454
				}
455
 
456
				var listContainer = $('<div></div>').addClass('mapplic-list-container').appendTo(this.el);
457
				this.list = $('<ol></ol>').addClass('mapplic-list').appendTo(listContainer);
458
				this.notfound = $('<p></p>').addClass('mapplic-not-found').text('Nothing found. Please try a different search.').appendTo(listContainer);
459
 
460
				if (!self.o.search) listContainer.css('padding-top', '0');
461
			}
462
 
463
			this.addCategories = function(categories) {
464
				var list = this.list;
465
 
466
				if (categories) {
467
					$.each(categories, function(index, category) {
468
						var item = $('<li></li>').addClass('mapplic-list-category').attr('data-category', category.id);
469
						var ol = $('<ol></ol>').css('border-color', category.color).appendTo(item);
470
						if (category.show == 'false') ol.hide();
471
						else item.addClass('mapplic-opened');
472
						var link = $('<a></a>').attr('href', '#').attr('title', category.title).css('background-color', category.color).text(category.title).prependTo(item);
473
						link.on('click', function(e) {
474
							e.preventDefault();
475
							item.toggleClass('mapplic-opened');
476
							ol.slideToggle(200);
477
						});
478
						if (category.icon) $('<img>').attr('src', category.icon).addClass('mapplic-list-thumbnail').prependTo(link);
479
						$('<span></span>').text('0').addClass('mapplic-list-count').prependTo(link);
480
						list.append(item);
481
					});
482
					}
483
			}
484
 
485
			this.addLocation = function(data) {
486
				var item = $('<li></li>').addClass('mapplic-list-location').addClass('mapplic-list-shown');
487
				var link = $('<a></a>').attr('href', '#').click(function(e) {
488
					e.preventDefault();
489
					showLocation(data.id, 600);
490
 
491
					// Scroll back to map on mobile
492
					if ($(window).width() < 668) {
493
						$('html, body').animate({
494
							scrollTop: self.container.offset().top
495
						}, 400);
496
					}
497
				}).appendTo(item);
498
 
499
				if (data.thumbnail) $('<img>').attr('src', data.thumbnail).addClass('mapplic-list-thumbnail').appendTo(link);
500
				$('<h4></h4>').text(data.title).appendTo(link)
501
				$('<span></span>').html(data.about).appendTo(link);
502
				var category = $('.mapplic-list-category[data-category="' + data.category + '"]');
503
 
504
				if (category.length) $('ol', category).append(item);
505
				else this.list.append(item);
506
 
507
				// Count
508
				$('.mapplic-list-count', category).text($('.mapplic-list-shown', category).length);
509
			}
510
 
511
			this.search = function(keyword) {
512
				if (keyword) self.clear.fadeIn(100);
513
				else self.clear.fadeOut(100);
514
 
515
				$('.mapplic-list li', self.el).each(function() {
516
					if ($(this).text().search(new RegExp(keyword, "i")) < 0) {
517
						$(this).removeClass('mapplic-list-shown');
518
						$(this).slideUp(200);
519
					} else {
520
						$(this).addClass('mapplic-list-shown');
521
						$(this).show();
522
					}
523
				});
524
 
525
				$('.mapplic-list > li', self.el).each(function() {
526
					var count = $('.mapplic-list-shown', this).length;
527
					$('.mapplic-list-count', this).text(count);
528
				});
529
 
530
				// Show not-found text
531
				if ($('.mapplic-list > li.mapplic-list-shown').length > 0) this.notfound.fadeOut(200);
532
				else this.notfound.fadeIn(200);
533
			}
534
		}
535
 
536
		// Developer tools
537
		function DevTools() {
538
			this.el = null;
539
 
540
			this.init = function() {
541
				this.el = $('<div></div>').addClass('mapplic-coordinates').appendTo(self.container);
542
				this.el.append('x: ');
543
				$('<code></code>').addClass('mapplic-coordinates-x').appendTo(this.el);
544
				this.el.append(' y: ');
545
				$('<code></code>').addClass('mapplic-coordinates-y').appendTo(this.el);
546
 
547
				$('.mapplic-layer', self.map).on('mousemove', function(e) {
548
					var x = (e.pageX - self.map.offset().left) / self.map.width(),
549
						y = (e.pageY - self.map.offset().top) / self.map.height();
550
					$('.mapplic-coordinates-x').text(parseFloat(x).toFixed(4));
551
					$('.mapplic-coordinates-y').text(parseFloat(y).toFixed(4));
552
				});
553
			}
554
		}
555
 
556
		// Clear Button
557
		function ClearButton() {
558
			this.el = null;
559
 
560
			this.init = function() {
561
				this.el = $('<a></a>').attr('href', '#').addClass('mapplic-clear-button').appendTo(self.container);
562
 
563
				this.el.on('click touchstart', function(e) {
564
					e.preventDefault();
565
					if (self.deeplinking) self.deeplinking.clear();
566
					$('.mapplic-active', self.el).attr('class', 'mapplic-clickable');
567
					self.tooltip.hide();
568
					zoomTo(0.5, 0.5, 1, 400, 'easeInOutCubic');
569
				});
570
			}
571
		}
572
 
573
		// Zoom Buttons
574
		function ZoomButtons() {
575
			this.el = null;
576
 
577
			this.init = function() {
578
				this.el = $('<div></div>').addClass('mapplic-zoom-buttons').appendTo(self.container);
579
 
580
				this.zoomin = $('<a></ha>').attr('href', '#').addClass('mapplic-zoomin-button').appendTo(this.el);
581
 
582
				this.zoomin.on('click touchstart', function(e) {
583
					e.preventDefault();
584
 
585
					var scale = self.scale;
586
					self.scale = normalizeScale(scale + scale * 0.8);
587
 
588
					self.x = normalizeX(self.x - (self.container.width()/2 - self.x) * (self.scale/scale - 1));
589
					self.y = normalizeY(self.y - (self.container.height()/2 - self.y) * (self.scale/scale - 1));
590
 
591
					moveTo(self.x, self.y, self.scale, 400, 'easeInOutCubic');
592
				});
593
 
594
				this.zoomout = $('<a></ha>').attr('href', '#').addClass('mapplic-zoomout-button').appendTo(this.el);
595
 
596
				this.zoomout.on('click touchstart', function(e) {
597
					e.preventDefault();
598
 
599
					var scale = self.scale;
600
					self.scale = normalizeScale(scale - scale * 0.4);
601
 
602
					self.x = normalizeX(self.x - (self.container.width()/2 - self.x) * (self.scale/scale - 1));
603
					self.y = normalizeY(self.y - (self.container.height()/2 - self.y) * (self.scale/scale - 1));
604
 
605
					moveTo(self.x, self.y, self.scale, 400, 'easeInOutCubic');
606
				});
607
			}
608
 
609
			this.update = function(scale) {
610
				this.zoomin.removeClass('mapplic-disabled');
611
				this.zoomout.removeClass('mapplic-disabled');
612
				if (scale == self.fitscale) this.zoomout.addClass('mapplic-disabled');
613
				else if (scale == self.o.maxscale) this.zoomin.addClass('mapplic-disabled');
614
			}
615
		}
616
 
617
		// Full Screen
618
		function FullScreen() {
619
			this.el = null;
620
 
621
			this.init = function() {
622
				var s = this;
623
				this.element = self.el[0];
624
 
625
				$('<a></a>').attr('href', '#').attr('href', '#').addClass('mapplic-fullscreen-button').click(function(e) {
626
					e.preventDefault();
627
 
628
					if (s.isFull()) s.exitFull();
629
					else s.goFull();
630
 
631
				}).appendTo(self.container);
632
			}
633
 
634
			this.goFull = function() {
635
				if (this.element.requestFullscreen) this.element.requestFullscreen();
636
				else if(this.element.mozRequestFullScreen) this.element.mozRequestFullScreen();
637
				else if(this.element.webkitRequestFullscreen) this.element.webkitRequestFullscreen();
638
				else if(this.element.msRequestFullscreen) this.element.msRequestFullscreen();
639
			}
640
 
641
			this.exitFull = function() {
642
				if (document.exitFullscreen) document.exitFullscreen();
643
				else if(document.mozCancelFullScreen) document.mozCancelFullScreen();
644
				else if(document.webkitExitFullscreen) document.webkitExitFullscreen();
645
			}
646
 
647
			this.isFull = function() {
648
				if (window.innerHeight == screen.height) {
649
					return true;
650
				} else {
651
					return false;
652
				}
653
			}
654
		}
655
 
656
		// Functions
657
		var processData = function(data) {
658
			self.data = data;
659
			var nrlevels = 0;
660
			var shownLevel;
661
 
662
			self.container = $('<div></div>').addClass('mapplic-container').appendTo(self.el);
663
			self.map = $('<div></div>').addClass('mapplic-map').appendTo(self.container);
664
			if (self.o.zoom) self.map.addClass('mapplic-zoomable');
665
 
666
			self.levelselect = $('<select></select>').addClass('mapplic-levels-select');
667
 
668
			if (!self.o.sidebar) self.container.css('width', '100%');
669
 
670
			self.contentWidth = parseInt(data.mapwidth);
671
			self.contentHeight = parseInt(data.mapheight);
672
 
673
			self.hw_ratio = data.mapheight / data.mapwidth;
674
 
675
			self.map.css({
676
				'width': data.mapwidth,
677
				'height': data.mapheight
678
			});
679
 
680
			// Create minimap
681
			if (self.o.minimap) {
682
				self.minimap = new Minimap();
683
				self.minimap.init();
684
			}
685
 
686
			// Create sidebar
687
			if (self.o.sidebar) {
688
				self.sidebar = new Sidebar();
689
				self.sidebar.init();
690
				self.sidebar.addCategories(data.categories);
691
			}
692
 
693
			// Iterate through levels
694
			if (data.levels) {
695
				$.each(data.levels, function(index, value) {
696
					var source = value.map;
697
					var extension = source.substr((source.lastIndexOf('.') + 1)).toLowerCase();
698
 
699
					// Create new map layer
700
					var layer = $('<div></div>').addClass('mapplic-layer').addClass(value.id).hide().appendTo(self.map);
701
					switch (extension) {
702
 
703
						// Image formats
704
						case 'jpg': case 'jpeg': case 'png': case 'gif':
705
							$('<img>').attr('src', source).addClass('mapplic-map-image').appendTo(layer);
706
							break;
707
 
708
						// Vector format
709
						case 'svg':
710
							$('<div></div>').addClass('mapplic-map-image').load(source, function() {
711
								// setting up the location on the map
712
								$(self.o.selector, this).each(function() {
713
									var location = getLocationData($(this).attr('id'));
714
									if (location) {
715
										$(this).attr('class', 'mapplic-clickable');
716
										location.onmap = $(this);
717
 
718
										if (location.fill) {
719
											$(this).css('fill', location.fill);
720
											$('path', this).css('fill', location.fill);
721
										}
722
 
723
										// Landmark mode
724
										if (self.o.landmark === location.id) $(this).attr('class', 'mapplic-active');
725
									}
726
								});
727
 
728
								// click event
729
								$(self.o.selector).on('click touchend', function() {
730
									if (!self.dragging) {
731
										var id = $(this).attr('id');
732
										showLocation(id, 600);
733
									}
734
								});
735
 
736
								// Support for the old map format
737
								$('svg a', this).each(function() {
738
									var location = getLocationData($(this).attr('xlink:href').substr(1));
739
									if (location) {
740
										$(this).attr('class', 'mapplic-clickable');
741
										location.onmap = $(this);
742
									}
743
								});
744
 
745
								$('svg a', this).click(function(e) {
746
									var id = $(this).attr('xlink:href').substr(1);
747
									showLocation(id, 600);
748
									e.preventDefault();
749
								});
750
							}).appendTo(layer);
751
							break;
752
 
753
						// Other
754
						default:
755
							alert('File type ' + extension + ' is not supported!');
756
					}
757
 
758
					// Create new minimap layer
759
					if (self.minimap) self.minimap.addLayer(value);
760
 
761
					// Build layer control
762
					self.levelselect.prepend($('<option></option>').attr('value', value.id).text(value.title));
763
 
764
					if (!shownLevel || value.show) {
765
						shownLevel = value.id;
766
					}
767
 
768
					// Iterate through locations
769
					$.each(value.locations, function(index, value) {
770
						var top = value.y * 100;
771
						var left = value.x * 100;
772
 
773
						if (value.pin != 'hidden') {
774
							if (self.o.markers) {
775
								var target = '#';
776
								if (value.action == 'redirect') target = value.link;
777
 
778
								var pin = $('<a></a>').attr('href', target).addClass('mapplic-pin').css({'top': top + '%', 'left': left + '%'}).appendTo(layer);
779
								pin.on('click touchend', function(e) {
780
									e.preventDefault();
781
									showLocation(value.id, 600);
782
								});
783
								if (value.fill) pin.css('background-color', value.fill);
784
								pin.attr('data-location', value.id);
785
								pin.addClass(value.pin);
786
							}
787
						}
788
 
789
						if (self.sidebar) self.sidebar.addLocation(value);
790
					});
791
 
792
					nrlevels++;
793
				});
794
			}
795
 
796
			// COMPONENTS
797
 
798
			// Tooltip
799
			self.tooltip = new Tooltip();
800
			self.tooltip.init();
801
 
802
			// Hover Tooltip
803
			if (self.o.hovertip) {
804
				self.hovertip = new HoverTooltip();
805
				self.hovertip.init();
806
			}
807
 
808
			// Developer tools
809
			if (self.o.developer) self.devtools = new DevTools().init();
810
 
811
			// Clear button
812
			if (self.o.clearbutton) self.clearbutton = new ClearButton().init();
813
 
814
			// Zoom buttons
815
			if (self.o.zoombuttons) {
816
				self.zoombuttons = new ZoomButtons();
817
				self.zoombuttons.init();
818
				if (!self.o.clearbutton) self.zoombuttons.el.css('bottom', '0');
819
			}
820
 
821
			// Fullscreen
822
			if (self.o.fullscreen) self.fullscreen = new FullScreen().init();
823
 
824
			// Levels
825
			if (nrlevels > 1) {
826
				self.levels = $('<div></div>').addClass('mapplic-levels');
827
				var up = $('<a href="#"></a>').addClass('mapplic-levels-up').appendTo(self.levels);
828
				self.levelselect.appendTo(self.levels);
829
				var down = $('<a href="#"></a>').addClass('mapplic-levels-down').appendTo(self.levels);
830
				self.container.append(self.levels);
831
 
832
				self.levelselect.change(function() {
833
					var value = $(this).val();
834
					switchLevel(value);
835
				});
836
 
837
				up.click(function(e) {
838
					e.preventDefault();
839
					if (!$(this).hasClass('mapplic-disabled')) switchLevel('+');
840
				});
841
 
842
				down.click(function(e) {
843
					e.preventDefault();
844
					if (!$(this).hasClass('mapplic-disabled')) switchLevel('-');
845
				});
846
			}
847
			switchLevel(shownLevel);
848
 
849
			// Browser resize
850
			$(window).resize(function() {
851
				// Mobile
852
				if ($(window).width() < 668) {
853
					self.container.height($(window).height() - 66);
854
				}
855
				else self.container.height('100%');
856
 
857
				var wr = self.container.width() / self.contentWidth,
858
					hr = self.container.height() / self.contentHeight;
859
 
860
				if (self.o.mapfill) {
861
					if (wr > hr) self.fitscale = wr;
862
					else self.fitscale = hr;
863
				}
864
				else {
865
					if (wr < hr) self.fitscale = wr;
866
					else self.fitscale = hr;
867
				}
868
 
869
				self.scale = normalizeScale(self.scale);
870
				self.x = normalizeX(self.x);
871
				self.y = normalizeY(self.y);
872
 
873
				moveTo(self.x, self.y, self.scale, 100);
874
 
875
			}).resize();
876
 
877
			// Landmark mode
878
			if (self.o.landmark) {
879
				showLocation(self.o.landmark, 0);
880
			}
881
			else {
882
				zoomTo(0.5, 0.5, 1, 0);
883
			}
884
 
885
			// Deeplinking
886
			if (self.o.deeplinking) {
887
				if (history.pushState) self.deeplinking = new Deeplinking();
888
				else self.deeplinking = new DeeplinkingHash();
889
 
890
				self.deeplinking.init();
891
			}
892
 
893
			// Controls
894
			if (self.o.zoom) addControls();
895
		}
896
 
897
		var addControls = function() {
898
			var map = self.map,
899
				mapbody = $('.mapplic-map-image', self.map);
900
 
901
			document.ondragstart = function() { return false; } // IE drag fix
902
 
903
			// Drag & drop
904
			mapbody.on('mousedown', function(event) {
905
				self.dragging = false;
906
				map.stop();
907
 
908
				map.data('mouseX', event.pageX);
909
				map.data('mouseY', event.pageY);
910
				map.data('lastX', self.x);
911
				map.data('lastY', self.y);
912
 
913
				map.addClass('mapplic-dragging');
914
 
915
				self.map.on('mousemove', function(event) {
916
					self.dragging = true;
917
 
918
					var x = event.pageX - map.data('mouseX') + self.x;
919
						y = event.pageY - map.data('mouseY') + self.y;
920
 
921
					x = normalizeX(x);
922
					y = normalizeY(y);
923
 
924
					moveTo(x, y);
925
					map.data('lastX', x);
926
					map.data('lastY', y);
927
				});
928
 
929
				$(document).on('mouseup', function(event) {
930
					self.x = map.data('lastX');
931
					self.y = map.data('lastY');
932
 
933
					self.map.off('mousemove');
934
					$(document).off('mouseup');
935
 
936
					map.removeClass('mapplic-dragging');
937
				});
938
			});
939
 
940
			// Double click
941
			$(document).on('dblclick', '.mapplic-map-image', function(event) {
942
				event.preventDefault();
943
 
944
				var scale = self.scale;
945
				self.scale = normalizeScale(scale * 2);
946
 
947
				self.x = normalizeX(self.x - (event.pageX - self.container.offset().left - self.x) * (self.scale/scale - 1));
948
				self.y = normalizeY(self.y - (event.pageY - self.container.offset().top - self.y) * (self.scale/scale - 1));
949
 
950
				moveTo(self.x, self.y, self.scale, 400, 'easeInOutCubic');
951
			});
952
 
953
			// Mousewheel
954
			$('.mapplic-layer', self.el).bind('mousewheel DOMMouseScroll', function(event, delta) {
955
				event.preventDefault();
956
 
957
				var scale = self.scale;
958
				self.scale = normalizeScale(scale + scale * delta/5);
959
 
960
				self.x = normalizeX(self.x - (event.pageX - self.container.offset().left - self.x) * (self.scale/scale - 1));
961
				self.y = normalizeY(self.y - (event.pageY - self.container.offset().top - self.y) * (self.scale/scale - 1));
962
 
963
				moveTo(self.x, self.y, self.scale, 200, 'easeOutCubic');
964
			});
965
 
966
			// Touch support
967
			if (!('ontouchstart' in window || 'onmsgesturechange' in window)) return true;
968
			mapbody.on('touchstart', function(e) {
969
				self.dragging = false;
970
 
971
				var orig = e.originalEvent,
972
					pos = map.position();
973
 
974
				map.data('touchY', orig.changedTouches[0].pageY - pos.top);
975
				map.data('touchX', orig.changedTouches[0].pageX - pos.left);
976
 
977
				mapbody.on('touchmove', function(e) {
978
					e.preventDefault();
979
					self.dragging = true;
980
 
981
					var orig = e.originalEvent;
982
					var touches = orig.touches.length;
983
 
984
					if (touches == 1) {
985
						self.x = normalizeX(orig.changedTouches[0].pageX - map.data('touchX'));
986
						self.y = normalizeY(orig.changedTouches[0].pageY - map.data('touchY'));
987
 
988
						moveTo(self.x, self.y, self.scale, 50);
989
					}
990
					else {
991
						mapbody.off('touchmove');
992
					}
993
				});
994
 
995
				mapbody.on('touchend', function(e) {
996
					mapbody.off('touchmove touchend');
997
				});
998
			});
999
 
1000
			// Pinch zoom
1001
			var hammer = new Hammer(self.map[0], {
1002
				transform_always_block: true,
1003
				drag_block_horizontal: true,
1004
				drag_block_vertical: true
1005
			});
1006
 
1007
			/* hammer fix */
1008
			self.map.on('touchstart', function(e) {
1009
				if (e.originalEvent.touches.length > 1) hammer.get('pinch').set({ enable: true });
1010
			});
1011
 
1012
			self.map.on('touchend', function(e) {
1013
				hammer.get('pinch').set({ enable: false });
1014
			});
1015
			/* hammer fix ends */
1016
 
1017
			var scale=1, last_scale;
1018
			hammer.on('pinchstart', function(e) {
1019
				self.dragging = false;
1020
 
1021
				scale = self.scale / self.fitscale;
1022
				last_scale = scale;
1023
			});
1024
 
1025
			hammer.on('pinch', function(e) {
1026
				self.dragging = true;
1027
 
1028
				if (e.scale != 1) scale = Math.max(1, Math.min(last_scale * e.scale, 100));
1029
 
1030
				var oldscale = self.scale;
1031
				self.scale = normalizeScale(scale * self.fitscale);
1032
 
1033
				self.x = normalizeX(self.x - (e.center.x - self.container.offset().left - self.x) * (self.scale/oldscale - 1));
1034
				self.y = normalizeY(self.y - (e.center.y - self.y) * (self.scale/oldscale - 1)); // - self.container.offset().top
1035
 
1036
				moveTo(self.x, self.y, self.scale, 100);
1037
			});
1038
		}
1039
 
1040
		var switchLevel = function(target, tooltip) {
1041
			switch (target) {
1042
				case '+':
1043
					target = $('option:selected', self.levelselect).removeAttr('selected').prev().prop('selected', 'selected').val();
1044
					break;
1045
				case '-':
1046
					target = $('option:selected', self.levelselect).removeAttr('selected').next().prop('selected', 'selected').val();
1047
					break;
1048
				default:
1049
					$('option[value="' + target + '"]', self.levelselect).prop('selected', 'selected');
1050
			}
1051
 
1052
			var layer = $('.mapplic-layer.' + target, self.map);
1053
 
1054
			// Target layer is active
1055
			if (layer.is(':visible')) return;
1056
 
1057
			// Hide Tooltip
1058
			if (!tooltip) self.tooltip.hide();
1059
 
1060
			// Show target layer
1061
			$('.mapplic-layer:visible', self.map).hide();
1062
			layer.show();
1063
 
1064
			// Show target minimap layer
1065
			if (self.minimap) self.minimap.show(target);
1066
 
1067
			// Update control
1068
			var index = self.levelselect.get(0).selectedIndex,
1069
				up = $('.mapplic-levels-up', self.levels),
1070
				down = $('.mapplic-levels-down', self.levels);
1071
 
1072
			up.removeClass('mapplic-disabled');
1073
			down.removeClass('mapplic-disabled');
1074
			if (index == 0) {
1075
				up.addClass('mapplic-disabled');
1076
			}
1077
			else if (index == self.levelselect.get(0).length - 1) {
1078
				down.addClass('mapplic-disabled');
1079
			}
1080
		}
1081
 
1082
		var getLocationData = function(id) {
1083
			var data = null;
1084
			$.each(self.data.levels, function(index, layer) {
1085
				$.each(layer.locations, function(index, value) {
1086
					if (value.id == id) {
1087
						data = value;
1088
					}
1089
				});
1090
			});
1091
			return data;
1092
		}
1093
 
1094
		var showLocation = function(id, duration, check) {
1095
			$.each(self.data.levels, function(index, layer) {
1096
				if (layer.id == id) {
1097
					switchLevel(layer.id, false);
1098
 
1099
					return false;
1100
				}
1101
				$.each(layer.locations, function(index, location) {
1102
					if (location.id == id) {
1103
						var ry = 0.5;
1104
 
1105
						if (!self.o.landmark) {
1106
							self.tooltip.set(location);
1107
							self.tooltip.show(location);
1108
							ry = ((self.container.height() - self.tooltip.drop) / 2 + self.tooltip.drop) / self.container.height();
1109
						}
1110
						var zoom = typeof location.zoom !== 'undefined' ? location.zoom : 4;
1111
 
1112
						switchLevel(layer.id, true);
1113
 
1114
						zoomTo(location.x, location.y, zoom, duration, 'easeInOutCubic', ry);
1115
 
1116
						$('.mapplic-active', self.el).attr('class', 'mapplic-clickable');
1117
						if (location.onmap) location.onmap.attr('class', 'mapplic-active');
1118
 
1119
						if ((self.o.deeplinking) && (!check)) self.deeplinking.update(id);
1120
 
1121
						return false;
1122
					}
1123
				});
1124
			});
1125
		};
1126
 
1127
		var normalizeX = function(x) {
1128
			var minX = self.container.width() - self.contentWidth * self.scale;
1129
 
1130
			if (minX < 0) {
1131
				if (x > 0) x = 0;
1132
				else if (x < minX) x = minX;
1133
			}
1134
			else x = minX/2;
1135
 
1136
			return x;
1137
		}
1138
 
1139
		var normalizeY = function(y) {
1140
			var minY = self.container.height() - self.contentHeight * self.scale;
1141
 
1142
			if (minY < 0) {
1143
				if (y >= 0) y = 0;
1144
				else if (y < minY) y = minY;
1145
			}
1146
			else y = minY/2;
1147
 
1148
			return y;
1149
		}
1150
 
1151
		var normalizeScale = function(scale) {
1152
			if (scale < self.fitscale) scale = self.fitscale;
1153
			else if (scale > self.o.maxscale) scale = self.o.maxscale;
1154
 
1155
			if (self.zoombuttons) self.zoombuttons.update(scale);
1156
 
1157
			return scale;
1158
		}
1159
 
1160
		var zoomTo = function(x, y, s, duration, easing, ry) {
1161
			duration = typeof duration !== 'undefined' ? duration : 400;
1162
			ry = typeof ry !== 'undefined' ? ry : 0.5;
1163
 
1164
			self.scale = normalizeScale(self.fitscale * s);
1165
 
1166
			self.x = normalizeX(self.container.width() * 0.5 - self.scale * self.contentWidth * x);
1167
			self.y = normalizeY(self.container.height() * ry - self.scale * self.contentHeight * y);
1168
 
1169
			moveTo(self.x, self.y, self.scale, duration, easing);
1170
		}
1171
 
1172
		var moveTo = function(x, y, scale, d, easing) {
1173
			if (scale !== undefined) {
1174
				self.map.stop().animate({
1175
					'left': x,
1176
					'top': y,
1177
					'width': self.contentWidth * scale,
1178
					'height': self.contentHeight * scale
1179
				}, d, easing, function() {
1180
					if (self.tooltip) self.tooltip.position();
1181
				});
1182
			}
1183
			else {
1184
				self.map.css({
1185
					'left': x,
1186
					'top': y
1187
				});
1188
			}
1189
			if (self.tooltip) self.tooltip.position();
1190
			if (self.minimap) self.minimap.update(x, y);
1191
		}
1192
	};
1193
 
1194
	//  Create a jQuery plugin
1195
	$.fn.mapplic = function(params) {
1196
		var len = this.length;
1197
 
1198
		return this.each(function(index) {
1199
			var me = $(this),
1200
				key = 'mapplic' + (len > 1 ? '-' + ++index : ''),
1201
				instance = (new Mapplic).init(me, params);
1202
		});
1203
	};
1204
 
1205
})(jQuery);