Issues (75)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

js/script.js (50 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
/**
2
 * ownCloud - maps
3
 *
4
 * This file is licensed under the Affero General Public License version 3 or
5
 * later. See the COPYING file.
6
 *
7
 * @author Sander Brand <[email protected]>
8
 * @copyright Sander Brand 2014
9
 */
10
/** Testing
11
 */
12
13
Array.prototype.clean = function(deleteValue) {
14
	for (var i = 0; i < this.length; i++) {
15
		if (this[i] == deleteValue) {
16
			this.splice(i, 1);
17
			i--;
18
		}
19
	}
20
	return this;
21
};
22
Array.prototype.unique = function() {
23
	var unique = [];
24
	for (var i = 0; i < this.length; i++) {
25
		if (unique.indexOf(this[i]) == -1) {
26
			unique.push(this[i]);
27
		}
28
	}
29
	return unique;
30
};
31
32
function debounce(func, wait, immediate) {
33
	var timeout;
34
	return function() {
35
		var context = this;
36
		var args = arguments;
37
		var later = function() {
38
			timeout = null;
39
			if(!immediate) func.apply(context, args);
40
		}
0 ignored issues
show
There should be a semicolon.

Requirement of semicolons purely is a coding style issue since JavaScript has specific rules about semicolons which are followed by all browsers.

Further Readings:

Loading history...
41
		var callNow = immediate && !timeout;
42
		clearTimeout(timeout);
43
		timeout = setTimeout(later, wait);
44
		if(callNow) func.apply(context, args);
45
	}
0 ignored issues
show
There should be a semicolon.

Requirement of semicolons purely is a coding style issue since JavaScript has specific rules about semicolons which are followed by all browsers.

Further Readings:

Loading history...
46
}
47
48
(function($, OC) {
49
50
	var shadowMarkerUrl = OC.filePath('maps', 'img', 'icons/marker-shadow.svg');
51
	var normalMarkerImg = OC.filePath('maps', 'img', 'icons/marker-icon.svg');
52
	var normalMarkerIcon = L.icon({
53
		iconUrl : normalMarkerImg,
54
		shadowUrl : shadowMarkerUrl,
55
		iconSize : [32, 32],
56
		iconAnchor : [16, 32],
57
		popupAnchor : [0, -32],
58
		shadowAnchor : [6, 23]
59
	});
60
61
	var favMarkerImg = OC.filePath('maps', 'img', 'icons/favMarker.svg');
62
	var favMarkerIcon = L.icon({
63
		iconUrl : favMarkerImg,
64
		shadowUrl : shadowMarkerUrl,
65
		iconSize : [32, 32],
66
		iconAnchor : [16, 32],
67
		popupAnchor : [0, -32],
68
		shadowAnchor : [6, 23]
69
	});
70
71
	function addGeocodeMarker(latlng) {
72
		Maps.droppedPin = new L.marker(latlng, {icon: normalMarkerIcon});
73
		geocoder.reverse(latlng, 67108864, function(results) {
74
			var result = results[0];
75
			setTimeout(function() {
76
				var popupHtml = '';
77
				var properties = result.properties;
78
				var address = properties;
79
				if(!geocodeSearch.apiKeySet()) address = properties.address; //address is stored depending on which geocoder is used
80
				popupHtml = Maps.getPoiPopupHTML(address).innerHTML;
81
				toolKit.addMarker(Maps.droppedPin, popupHtml, true);
82
			}, 50);
83
84
		})
0 ignored issues
show
There should be a semicolon.

Requirement of semicolons purely is a coding style issue since JavaScript has specific rules about semicolons which are followed by all browsers.

Further Readings:

Loading history...
85
	}
86
87
	$.fn.clickToggle = function(func1, func2) {
88
		var funcs = [func1, func2];
89
		this.data('toggleclicked', 0);
90
		this.click(function() {
91
			var data = $(this).data();
92
			var tc = data.toggleclicked;
93
			$.proxy(funcs[tc], this)();
94
			data.toggleclicked = (tc + 1) % 2;
95
		});
96
		return this;
97
	};
98
99
	// initialize map when page ready
100
	$(document).ready(function() {
101
		marker = null;
102
		circle = null;
103
		firstRun = true;
104
105
		var attribution = '&copy; <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>';
106
107
		var mapQuest = L.tileLayer('http://otile{s}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png', {
108
			attribution : attribution,
109
			subdomains : "1234"
110
		})
0 ignored issues
show
There should be a semicolon.

Requirement of semicolons purely is a coding style issue since JavaScript has specific rules about semicolons which are followed by all browsers.

Further Readings:

Loading history...
111
		/*	var mapbox = L.tileLayer('https://a.tiles.mapbox.com/v3/liedman.h9ekn0f1/{z}/{x}/{y}.png', {
112
		 attribution : attribution + ' Tiles <a href="https://www.mapbox.com/about/maps/">MapBox</a>'
113
		 })
114
		 var blackAndWhite = L.tileLayer('http://{s}.www.toolserver.org/tiles/bw-mapnik/{z}/{x}/{y}.png', {
115
		 attribution : attribution
116
		 })
117
118
		 var airial = L.tileLayer('http://server.arcgisonline.com/ArcGIS/' + 'rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
119
		 attribution : attribution + ' Tiles © Esri',
120
		 subdomains : "1234"
121
		 })*/
122
123
		/*var clouds = L.tileLayer('http://{s}.tile.openweathermap.org/map/clouds/{z}/{x}/{y}.png', {
124
		 attribution : 'Map data &copy; <a href="http://openweathermap.org">OpenWeatherMap</a>',
125
		 opacity : 0.5
126
		 })
127
		 var wind = L.tileLayer('http://{s}.tile.openweathermap.org/map/wind/{z}/{x}/{y}.png', {
128
		 attribution : 'Map data &copy; <a href="http://openweathermap.org">OpenWeatherMap</a>',
129
		 opacity : 0.5
130
		 })
131
		 var temperature = L.tileLayer('http://{s}.tile.openweathermap.org/map/temp/{z}/{x}/{y}.png', {
132
		 attribution : 'Map data &copy; <a href="http://openweathermap.org">OpenWeatherMap</a>',
133
		 opacity : 0.5
134
		 })
135
136
		 var seamarks = L.tileLayer('http://tiles.openseamap.org/seamark/{z}/{x}/{y}.png', {
137
		 attribution : 'Map data &copy; <a href="http://openweathermap.org">OpenSeaMap</a>',
138
		 opacity : 1.0
139
		 })
140
141
		 var none = L.tileLayer('http://{s}.tile.openweathermap.org/map/temp/{z}/{x}/{y}.png', {
142
		 attribution : '',
143
		 opacity : 0.0
144
		 })*/
145
146
		var oldPosition = $.jStorage.get('location', {
147
			lat : 21.303210151521565,
148
			lng : 6.15234375
149
		});
150
		var oldZoom = $.jStorage.get('zoom', 3);
151
152
		map = L.map('map', {
153
			center : new L.LatLng(oldPosition.lat, oldPosition.lng),
154
			zoom : oldZoom,
155
			zoomControl : false,
156
			layers : [mapQuest]
157
		}),
158
159
		map.options.minZoom = 3;
0 ignored issues
show
Did you forget to assign or call a function?

This error message can for example pop up if you forget to assign the result of a function call to a variable or pass it to another function:

function someFunction(x) {
    (x > 0) ? callFoo() : callBar();
}

// JSHint expects you to assign the result to a variable:
function someFunction(x) {
    var rs = (x > 0) ? callFoo() : callBar();
}

// If you do not use the result, you could also use if statements in the
// case above.
function someFunction(x) {
    if (x > 0) {
        callFoo();
    } else {
        callBar();
    }
}
Loading history...
160
		var hash = new L.Hash(map);
161
		/*var baseMaps = {
162
		 "MapBox" : mapbox,
163
		 "Mapnik" : mapnik,
164
		 "Black and White" : blackAndWhite,
165
		 "Airial" : airial
166
		 };*/
167
168
		//map.addControl(new L.Control.Compass());
169
		/*map.addControl(new L.Control.Zoom({
170
			position: 'bottomright'
171
		}));*/
172
		map.addControl(new L.Control.Gps({
173
			minZoom : 14,
174
			autoActive: 1,
175
			style : {
176
				radius : 16, //marker circle style
177
				weight : 3,
178
				color : '#0A00FF',
179
				fill : true
180
			},
181
			position : 'topleft',
182
		}));
183
		$('.leaflet-control-layers-overlays').removeProp('multiple');
184
		map.on('popupopen', function(e) {
185
			currentMarker = e.popup._source;
186
		});
187
		routing = L.Routing.control({
188
			waypoints : [],
189
			plan : L.Routing.plan(this.waypoints, {
190
			    createMarker: function(i, wp) {
191
				    return L.marker(wp.latLng, {
192
					    draggable: true,
193
					    icon: normalMarkerIcon
194
				    });
195
			    },
196
			    routeWhileDragging: true
197
		    }),
198
			//geocoder : geocoder,
199
			routeWhileDragging: true,
200
			fitSelectedRoutes: true,
201
			show: false
202
		}).addTo(map);
203
204
		routing.getPlan().on('waypointschanged', function(e) {
205
			geocodeSearch.waypointsChanged(e);
206
		})
0 ignored issues
show
There should be a semicolon.

Requirement of semicolons purely is a coding style issue since JavaScript has specific rules about semicolons which are followed by all browsers.

Further Readings:

Loading history...
207
208
		apiKey = '';
209
210
		geocodeSearch.addGeocoder();
211
212
		// properly style as input field
213
		//$('#searchContainer').find('input').attr('type', 'text');
214
215
		map.on('zoomend', function(e) {
216
			var zoom = map.getZoom();
217
			if(!$.jStorage.get('pois') || zoom < 9) return; //only show POIs on reasonable levels
218
			Maps.displayPoiIcons(zoom);
219
		})
0 ignored issues
show
There should be a semicolon.

Requirement of semicolons purely is a coding style issue since JavaScript has specific rules about semicolons which are followed by all browsers.

Further Readings:

Loading history...
220
221
		$.post(OC.generateUrl('/apps/maps/api/1.0/apikey/getKey'), null, function(data){
222
			if(data.id != null && data.apiKey != null) {
0 ignored issues
show
It is recommended to use !== to compare with null.

Generally, it is recommended to use strict comparison whenever possible and not to rely on the weaker type-juggling comparison operator.

Read more about comparison operations.

Loading history...
223
				apiKey = data.apiKey;
224
				document.getElementById('apiKey').value = apiKey;
225
				geocoder = L.Control.Geocoder.mapzen(apiKey);
226
			} else {
227
				geocoder = L.Control.Geocoder.nominatim();
228
			}
229
		});
230
231
		map.on('mousedown', function(e) {
232
			Maps.mouseDowntime = new Date().getTime();
233
		});
234
235
		map.on('mouseup', function(e) {
236
			if (e.originalEvent.target.className !== 'leaflet-tile leaflet-tile-loaded' && e.originalEvent.target.nodeName != 'svg')
237
				return;
238
239
			var curTime = new Date().getTime();
240
			if (Maps.droppedPin) {
241
				map.removeLayer(Maps.droppedPin);
242
				Maps.droppedPin = false;
243
			}
244
			if (/*(curTime - Maps.mouseDowntime) > 200 && */Maps.dragging === false) {//200 = 2 seconds
245
				addGeocodeMarker(e.latlng);
246
			}
247
		});
248
249
		map.on("dragstart", function() {
250
			Maps.dragging = true;
251
		})
0 ignored issues
show
There should be a semicolon.

Requirement of semicolons purely is a coding style issue since JavaScript has specific rules about semicolons which are followed by all browsers.

Further Readings:

Loading history...
252
		map.on("dragend zoomend", function(e) {
253
			Maps.saveCurrentLocation(e);
254
			Maps.dragging = false;
255
		})
0 ignored issues
show
There should be a semicolon.

Requirement of semicolons purely is a coding style issue since JavaScript has specific rules about semicolons which are followed by all browsers.

Further Readings:

Loading history...
256
257
		$(document).on('click', '.toggle-children', function(e) {
258
			var subCat = $(this).parent().find('ul');
259
			if (subCat.is(":visible")) {
260
				subCat.slideUp();
261
			} else {
262
				subCat.removeClass('hidden');
263
				subCat.slideDown();
264
			}
265
		})
0 ignored issues
show
There should be a semicolon.

Requirement of semicolons purely is a coding style issue since JavaScript has specific rules about semicolons which are followed by all browsers.

Further Readings:

Loading history...
266
267
		function convertLatLon(latD, lonD, latDir, lonDir){
268
			var lon = lonD[0] + lonD[1]/60 + lonD[2]/(60*60);
269
			var lat = latD[0] + latD[1]/60 + latD[2]/(60*60);
270
			if (latDir == "S") {
271
				lat = lat * -1;
272
			}
273
			if(lonDir == "W"){
274
				lon = lon * -1;
275
			}
276
			return {
277
				lat: lat,
278
				lon: lon
279
			};
280
		}
281
		$('.photoLayer').clickToggle(function() {
282
			OC.dialogs.filepicker("Select your photo", function addJpegFile(path){
283
				for(i=0; i<path.length; i++){
284
					var p = path[i];
285
					if(p.indexOf('/') === 0) p = p.substr(1);
286
					var separator = p.lastIndexOf('/');
287
					var dir = p.substr(0, separator);
288
					var file = p.substr(separator + 1);
289
					var picUrl = OC.generateUrl('apps/files/ajax/download.php?dir={dir}&files={file}', {
290
						dir: dir,
291
						file: file
292
					});
293
					new ImageInfo(
294
						picUrl,
295
						(function (element){
0 ignored issues
show
It is generally not recommended to make functions within a loop.

While making functions in a loop will not lead to any runtime error, the code might not behave as you expect as the variables in the scope are not imported by value, but by reference. Let’s take a look at an example:

var funcs = [];
for (var i=0; i<10; i++) {
    funcs.push(function() {
        alert(i);
    });
}

funcs[0](); // alert(10);
funcs[1](); // alert(10);
/// ...
funcs[9](); // alert(10);

If you would instead like to bind the function inside the loop to the value of the variable during that specific iteration, you can create the function from another function:

var createFunc = function(i) {
    return function() {
        alert(i);
    };
};

var funcs = [];
for (var i=0; i<10; i++) {
    funcs.push(createFunc(i));
}

funcs[0](); // alert(0)
funcs[1](); // alert(1)
// ...
funcs[9](); // alert(9)
Loading history...
296
							return function (imageinfo){
297
								var exif = imageinfo.getAllFields().exif;
298
								var latD = exif.GPSLatitude;
299
								var lonD = exif.GPSLongitude;
300
								var latDir = exif.GPSLatitudeRef;
301
								var lonDir = exif.GPSLongitudeRef;
302
								var latlon = convertLatLon(latD, lonD, latDir, lonDir);
303
								var photoIcon = L.icon({
304
									iconUrl: picUrl,
305
									iconSize : [42, 49],
306
									iconAnchor : [21, 49],
307
									popupAnchor : [0, -49],
308
									className : 'photo-marker'
309
								});
310
311
								var markerHTML = '<h2>' + file + "</h2>";
312
								/*markerHTML += '<br />Latitude: ' + latlon.lat + " " + latDir;
313
								markerHTML += '<br />Longitude: ' + latlon.lon + " " + lonDir;
314
								markerHTML += '<br />Altitude: ' + exif.GPSAltitude + "m";*/
315
								var marker = L.marker([latlon.lat, latlon.lon], {
316
									icon : photoIcon
317
								});
318
								toolKit.addMarker(marker, markerHTML);
319
							};
320
						})(this)
321
					).readFileData();
322
				}
323
			}, true, ["image/jpeg", "image/png", "image/gif"], true);
324
		}, function() {
325
			//TODO
326
		});
327
328
		/* Favorites layer: Show by default, remember visibility */
329
		$('.favoriteLayer').click(function() {
330
			if($.jStorage.get('favorites')) {
331
				favorites.hide();
332
				$.jStorage.set('favorites', false);
333
				$('#favoriteMenu').removeClass('active').addClass('icon-star').removeClass('icon-starred');
334
			} else {
335
				favorites.show();
336
				$.jStorage.set('favorites', true);
337
				$('#favoriteMenu').addClass('active').addClass('icon-starred').removeClass('icon-star');
338
			}
339
		});
340
		if($.jStorage.get('favorites') === null) {
341
			favorites.show();
342
			$.jStorage.set('favorites', true);
343
			$('#favoriteMenu').addClass('active').addClass('icon-starred').removeClass('icon-star');
344
		}
345
		if($.jStorage.get('favorites')) {
346
			favorites.show();
347
			$('#favoriteMenu').addClass('active').addClass('icon-starred').removeClass('icon-star');
348
		} else {
349
			favorites.hide();
350
		}
351
352
		/* POI layer: Show by default, remember visibility */
353
		$('.poiLayer').click(function() {
354
			if($.jStorage.get('pois')) {
355
				Maps.hidePoiIcons();
356
				$.jStorage.set('pois', false);
357
				//$('#poiMenu').removeClass('active').addClass('icon-star').removeClass('icon-starred');
358
			} else {
359
				Maps.displayPoiIcons(map.getZoom());
360
				$.jStorage.set('pois', true);
361
				//$('#poiMenu').addClass('active').addClass('icon-starred').removeClass('icon-star');
362
			}
363
		});
364
		if($.jStorage.get('pois') === null) {
365
			Maps.displayPoiIcons(map.getZoom());
366
			$.jStorage.set('pois', true);
367
			//$('#poiMenu').addClass('active').addClass('icon-starred').removeClass('icon-star');
368
		}
369
		if($.jStorage.get('pois')) {
370
			Maps.displayPoiIcons(map.getZoom());
371
			//$('#poiMenu').addClass('active').addClass('icon-starred').removeClass('icon-star');
372
		} else {
373
			Maps.hidePoiIcons();
374
		}
375
376
377
		$('.contactLayer').clickToggle(function() {
378
			Maps.loadAdressBooks()
0 ignored issues
show
There should be a semicolon.

Requirement of semicolons purely is a coding style issue since JavaScript has specific rules about semicolons which are followed by all browsers.

Further Readings:

Loading history...
379
		}, function() {
380
			favorites.hide()
0 ignored issues
show
There should be a semicolon.

Requirement of semicolons purely is a coding style issue since JavaScript has specific rules about semicolons which are followed by all browsers.

Further Readings:

Loading history...
381
		});
382
		$(document).on('click', '.subLayer', function() {
383
			var layerGroup = $(this).attr('data-layerGroup');
384
			var layerValue = $(this).attr('data-layerValue');
385
			var isVisible = $(this).find('i').length;
386
387
			if (isVisible == 1) {
388
				$('.' + layerValue).css({
389
					'visibility' : 'hidden'
390
				})
0 ignored issues
show
There should be a semicolon.

Requirement of semicolons purely is a coding style issue since JavaScript has specific rules about semicolons which are followed by all browsers.

Further Readings:

Loading history...
391
				$(this).find('i').remove();
392
				Maps.updateLayers(false, false);
393
			} else {
394
				$('.' + layerValue).css({
395
					'visibility' : 'visible'
396
				})
0 ignored issues
show
There should be a semicolon.

Requirement of semicolons purely is a coding style issue since JavaScript has specific rules about semicolons which are followed by all browsers.

Further Readings:

Loading history...
397
				$(this).append('<i class="icon-toggle fright micon activeLayer"></i>');
398
				//if($('.'+layerValue).length==0){
399
				Maps.updateLayers(layerGroup, layerValue);
400
				/*} else{
401
				 Maps.updateLayers(false,false);
402
				 }*/
403
404
			}
405
		});
406
407
		$(document).on('click', '.device', function() {
408
			var isVisible = $(this).find('i').length;
409
			var dId = $(this).attr('data-deviceId')
0 ignored issues
show
There should be a semicolon.

Requirement of semicolons purely is a coding style issue since JavaScript has specific rules about semicolons which are followed by all browsers.

Further Readings:

Loading history...
410
			if (isVisible == 1) {
411
				$(this).find('i').remove();
412
				Maps.clearDevicePositions();
413
				Maps.clearDevicePosistionHistory(dId);
414
				var index = Maps.activeDevices.indexOf(dId);
415
				Maps.activeDevices.splice(index, 1);
416
				Maps.loadDevicesLastPosition();
417
			} else {
418
				Maps.activeDevices.push(dId);
419
				Maps.loadDevicesLastPosition();
420
				$(this).append('<i class="icon-toggle fright micon"></i>');
421
			}
422
		});
423
424
		$(document).on('click', '.keepDeviceCentered', function(e) {
425
			var isVisible = $(this).parent().find('i').length;
426
			var dId = $(this).parent().attr('data-deviceId')
0 ignored issues
show
There should be a semicolon.

Requirement of semicolons purely is a coding style issue since JavaScript has specific rules about semicolons which are followed by all browsers.

Further Readings:

Loading history...
427
			e.stopPropagation()
0 ignored issues
show
There should be a semicolon.

Requirement of semicolons purely is a coding style issue since JavaScript has specific rules about semicolons which are followed by all browsers.

Further Readings:

Loading history...
428
			if ($(this).hasClass('tracOn')) {
429
				$(this).removeClass('tracOn');
430
				Maps.traceDevice = null;
431
			} else {
432
				$('.keepDeviceCentered').removeClass('tracOn');
433
				$(this).addClass('tracOn');
434
				if (!isVisible) {
435
					Maps.activeDevices.push(dId);
436
					Maps.traceDevice = dId;
437
					Maps.loadDevicesLastPosition();
438
					$(this).parent().append('<i class="icon-toggle fright micon"></i>');
439
				} else {
440
					Maps.traceDevice = dId;
441
					Maps.loadDevicesLastPosition();
442
				}
443
			}
444
445
		});
446
447
		/**
448
		 * Setup datepickers
449
		 */
450
		$('.datetime').datetimepicker({
451
			dateFormat : 'dd-mm-yy',
452
			minDate : -900
453
		});
454
455
		$(document).on('click', '#setApiKey', function(e) {
456
			var value = document.getElementById('apiKey').value;
457
			if(value.length == 0) return;
0 ignored issues
show
It is recommended to use === to compare with 0.

Generally, it is recommended to use strict comparison whenever possible and not to rely on the weaker type-juggling comparison operator.

Read more about comparison operations.

Loading history...
458
			var formData = {
459
				key : value
460
			};
461
			$.post(OC.generateUrl('/apps/maps/api/1.0/apikey/addKey'), formData, function(data){
462
			});
463
		});
464
465
		$(document).on('click', '.deviceHistory', function(e) {
466
			var isVisible = $(this).parent().find('i').length;
467
			var dId = $(this).parent().attr('data-deviceId');
468
			var _this = this;
469
			e.stopPropagation();
470
			if (!isVisible) {
471
				$(".datetime").datepicker("disable");
472
				$('#showHistoryPopup').dialog({
473
					open : function() {
474
						$(".datetime").datepicker("enable");
475
						var currentDate = new Date();
476
						var month = ((currentDate.getMonth() * 1 + 1) < 10) ? '0' + (currentDate.getMonth() * 1 + 1) : (currentDate.getMonth() * 1 + 1);
477
						$('#deviceHistory [name="startDate"]').val(currentDate.getDate() + '-' + month + '-' + currentDate.getFullYear() + ' 00:00');
478
					},
479
					buttons : {
480
						"Cancel" : function() {
481
							$(this).dialog('destroy');
482
						},
483
						"Ok" : function() {
484
							var startDate = $('#deviceHistory [name="startDate"]').val();
485
							var endDate = $('#deviceHistory [name="endDate"]').val();
486
							var keepCenter = $('#deviceHistory [name="keepCenter"]').is(':checked');
487
							Maps.loadDevicePosistionHistory(dId, keepCenter, startDate, endDate);
488
							$(_this).parent().append('<i class="icon-toggle fright micon"></i>');
489
							$(this).dialog('destroy');
490
						}
491
					}
492
				});
493
			} else {
494
				Maps.clearDevicePosistionHistory(dId);
495
				$(this).parent().find('i').remove();
496
			}
497
498
		});
499
500
		/**
501
		 * Custom search function
502
		 */
503
		/*    searchItems = []*/
504
		searchTimeout = 0;
505
506
		/**
507
		 * setDestination on click
508
		 */
509
		map.locate({
510
			setView : false,
511
			watch : false
512
		})
0 ignored issues
show
There should be a semicolon.

Requirement of semicolons purely is a coding style issue since JavaScript has specific rules about semicolons which are followed by all browsers.

Further Readings:

Loading history...
513
		map.on('locationfound', function doRouteCalc(e) {
514
			currentlocation = [e.latitude, e.longitude];
515
516
		})
0 ignored issues
show
There should be a semicolon.

Requirement of semicolons purely is a coding style issue since JavaScript has specific rules about semicolons which are followed by all browsers.

Further Readings:

Loading history...
517
		$(document).on('click', '.setDestination', function() {
518
519
			var latlng = $(this).attr('data-latlng');
520
			var end = latlng.split(',');
521
			end[0] = end[0] * 1;
522
			end[1] = end[1] * 1;
523
			//map.removeLayer(routing);
524
525
			routing.setWaypoints([
526
				L.Routing.waypoint(L.latLng(currentlocation[0], currentlocation[1]), ""),
527
				L.Routing.waypoint(L.latLng(end[0], end[1]), "")
528
			]);
529
530
			map.closePopup();
531
		});
532
		/**
533
		 * Clear route
534
		 */
535
		$(document).on('click', '.clearroute', function() {
536
			routing.setWaypoints([]);
537
		});
538
539
	});
540
	// End document ready
541
	function onLocationFound(e) {
542
		var radius = e.accuracy / 2;
543
		if (marker != null) {
0 ignored issues
show
It is recommended to use !== to compare with null.

Generally, it is recommended to use strict comparison whenever possible and not to rely on the weaker type-juggling comparison operator.

Read more about comparison operations.

Loading history...
544
			map.removeLayer(marker);
545
		}
546
		if (circle != null) {
0 ignored issues
show
It is recommended to use !== to compare with null.

Generally, it is recommended to use strict comparison whenever possible and not to rely on the weaker type-juggling comparison operator.

Read more about comparison operations.

Loading history...
547
			map.removeLayer(circle);
548
		}
549
550
		marker = L.marker(e.latlng, {icon: normalMarkerIcon}).addTo(map);
551
		//.bindPopup("You are within " + radius + " meters from this point").openPopup();
552
		if (radius < 5000) {
553
			circle = L.circle(e.latlng, radius).addTo(map);
554
		}
555
556
		if (firstRun) {
557
			map.panTo(e.latlng);
558
			var maxZoom = 16;
559
			map.setZoom(14);
560
			firstRun = false;
561
		}
562
	}
563
564
	geocodeSearch = {
565
		results : [],
566
		waypoints : [],
567
		markers : [],
568
		apiKeySet : function() {
569
			return apiKey != null && apiKey.length > 0;
0 ignored issues
show
It is recommended to use !== to compare with null.

Generally, it is recommended to use strict comparison whenever possible and not to rely on the weaker type-juggling comparison operator.

Read more about comparison operations.

Loading history...
570
		},
571
		addGeocoder : function() {
572
			var geocoderInputs = document.getElementsByClassName('geocoder');
573
			var elems = 0;
574
			if(geocoderInputs != null) elems = geocoderInputs.length;
0 ignored issues
show
It is recommended to use !== to compare with null.

Generally, it is recommended to use strict comparison whenever possible and not to rely on the weaker type-juggling comparison operator.

Read more about comparison operations.

Loading history...
575
			if(elems == 7) return;//only allow 5 via points
576
			var timeoutId;
577
			var searchCont = document.getElementById('search');
578
			var geocoderDiv = document.createElement('div');
579
			geocoderDiv.className = 'geocoder-container';
580
			var input = document.createElement('input');
581
			input.type = 'text';
582
			input.className = 'geocoder not-geocoded';
583
			var debounceTimeout = 500;
584
			if(!geocodeSearch.apiKeySet()) debounceTimeout = 1000;
585
			input.addEventListener('input', debounce(function() {
586
				geocodeSearch.performGeocode(input)
0 ignored issues
show
There should be a semicolon.

Requirement of semicolons purely is a coding style issue since JavaScript has specific rules about semicolons which are followed by all browsers.

Further Readings:

Loading history...
587
			}, debounceTimeout));
588
			input.addEventListener('keyup', function(e) {
589
				var key = e.which || e.keyCode;
590
				if(key !== 13) return; //not enter/return key
591
				e.preventDefault();
592
				var result = null;
593
				if(geocodeSearch.results.length > 0) {
594
					result = geocodeSearch.results[0];
595
				} else {
596
					var results = geocodeSearch.performInlineGeocode(input);
597
					if(results != null && results.length > 0) result = results[0];
0 ignored issues
show
It is recommended to use !== to compare with null.

Generally, it is recommended to use strict comparison whenever possible and not to rely on the weaker type-juggling comparison operator.

Read more about comparison operations.

Loading history...
598
				}
599
				if(result == null) return;
0 ignored issues
show
It is recommended to use === to compare with null.

Generally, it is recommended to use strict comparison whenever possible and not to rely on the weaker type-juggling comparison operator.

Read more about comparison operations.

Loading history...
600
				input.className = input.className.replace('not-geocoded', 'is-geocoded');
601
				var marker = L.marker(result.center, {icon: normalMarkerIcon});
602
				geocodeSearch.markers.push([[input.id], [marker]]);
603
				toolKit.addMarker(marker, result.name);
604
				var points = document.getElementsByClassName('is-geocoded');
605
				if(points.length < 2 && result.bbox != null) {
0 ignored issues
show
It is recommended to use !== to compare with null.

Generally, it is recommended to use strict comparison whenever possible and not to rely on the weaker type-juggling comparison operator.

Read more about comparison operations.

Loading history...
606
					map.fitBounds(result.bbox);
607
				};
0 ignored issues
show
This semicolons seems to be unnecessary.
Loading history...
608
				input.value = result.name;
609
				geocodeSearch.clearResults(input);
610
				geocodeSearch.computeRoute();
611
			});
612
			input.addEventListener('blur', geocodeSearch.clearResults(input));
613
			var list = document.createElement('ul');
614
			list.className = 'geocoder-list';
615
			list.style.display = 'none';
616
617
			var btn = document.createElement('button');
618
619
			if(elems == 0) {
0 ignored issues
show
It is recommended to use === to compare with 0.

Generally, it is recommended to use strict comparison whenever possible and not to rely on the weaker type-juggling comparison operator.

Read more about comparison operations.

Loading history...
620
				geocoderDiv.id = 'geocoder-container-start';
621
				input.id = 'geocoder-start';
622
				list.id = 'geocoder-results-start';
623
				input.placeholder = 'Start address';
624
625
				btn.className = 'icon-add geocoder-button';
626
				btn.id = 'geocoder-add';
627
				btn.addEventListener('click', function() {
628
					geocodeSearch.addGeocoder();
629
				});
630
631
				geocoderDiv.appendChild(input);
632
				geocoderDiv.appendChild(list);
633
				geocoderDiv.appendChild(btn);
634
				searchCont.appendChild(geocoderDiv);
635
			} else if(elems == 1) {
636
				geocoderDiv.id = 'geocoder-container-end';
637
				input.id = 'geocoder-end';
638
				list.id = 'geocoder-results-end';
639
				input.placeholder = 'End address';
640
641
				btn.className = 'icon-close geocoder-button';
642
				btn.id = 'geocoder-remove-end';
643
				btn.addEventListener('click', function() {
644
					geocodeSearch.removeGeocoder(geocoderDiv);
645
				});
646
647
				geocoderDiv.appendChild(input);
648
				geocoderDiv.appendChild(list);
649
				geocoderDiv.appendChild(btn);
650
				searchCont.appendChild(geocoderDiv);
651
			} else {
652
				var id = elems - 2;
653
				geocoderDiv.id = 'geocoder-container-' + id;
654
				input.id = 'geocoder-' + id;
655
				list.id = 'geocoder-results-' + id;
656
				input.placeholder = 'Via address '/* + (id+1)*/;
657
658
				btn.className = 'icon-close geocoder-button';
659
				btn.id = 'geocoder-remove-' + id;
660
				btn.addEventListener('click', function() {
661
					geocodeSearch.removeGeocoder(geocoderDiv);
662
				});
663
664
				var children = searchCont.childNodes;
665
				var childLength = children.length;
666
				geocoderDiv.appendChild(input);
667
				geocoderDiv.appendChild(list);
668
				geocoderDiv.appendChild(btn);
669
				searchCont.insertBefore(geocoderDiv, children[childLength-1]);
670
			}
671
		},
672
		removeGeocoder : function(div) {
673
			var geocoderInputs = document.getElementsByClassName('geocoder');
674
			var elems = 0;
675
			if(geocoderInputs != null) elems = geocoderInputs.length;
0 ignored issues
show
It is recommended to use !== to compare with null.

Generally, it is recommended to use strict comparison whenever possible and not to rely on the weaker type-juggling comparison operator.

Read more about comparison operations.

Loading history...
676
			var isGeocoded = div.getElementsByClassName('is-geocoded');
677
			if(isGeocoded.length > 0) {
678
				geocodeSearch.removeMarker(isGeocoded[0].id)
0 ignored issues
show
There should be a semicolon.

Requirement of semicolons purely is a coding style issue since JavaScript has specific rules about semicolons which are followed by all browsers.

Further Readings:

Loading history...
679
			}
680
			//alert(div.getElementById('geocoder-end').toSource())
681
			if(div.id == 'geocoder-container-end') {
682
				var children = div.parentElement.childNodes;
683
				var childLength = children.length;
684
				var newEndDiv = children[childLength-2];
685
				var newInput = newEndDiv.getElementsByClassName('geocoder')[0];
686
				var newList = newEndDiv.getElementsByClassName('geocoder-list')[0];
687
				var newButton = newEndDiv.getElementsByClassName('geocoder-button')[0];
688
				newEndDiv.id = 'geocoder-container-end';
689
				newInput.id = 'geocoder-end';
690
				newList.id = 'geocoder-results-end';
691
				if(document.getElementsByClassName('geocoder').length - 1 > 1) {
692
					newInput.placeholder = 'End address';
693
				}
694
				newButton.id = 'geocoder-remove-end';
695
			}
696
			div.parentElement.removeChild(div);
697
		},
698
		removeMarker : function(id) {
699
			var remIndex = -1;
700
			for(var i=0; i<geocodeSearch.markers.length; ++i) {
701
				var curr = geocodeSearch.markers[i];
702
				if(curr[0] == id) {
703
					remIndex = i;
704
					map.removeLayer(curr[1][0]);
705
					break;
706
				}
707
			}
708
			if(remIndex >= 0) {
709
				geocodeSearch.markers.splice(remIndex, 1);
710
				geocodeSearch.computeRoute();
711
			}
712
		},
713
		clearResults : function(input) {
714
			geocodeSearch.results = [];
715
			var idParts = input.id.split('-');
716
			var id = idParts[0] + '-results-' + idParts[1];
717
			var list = document.getElementById(id);
718
			if(list == null) return;
0 ignored issues
show
It is recommended to use === to compare with null.

Generally, it is recommended to use strict comparison whenever possible and not to rely on the weaker type-juggling comparison operator.

Read more about comparison operations.

Loading history...
719
			list.innerHTML = '';
720
			list.style.display = 'none';
721
		},
722
		performInlineGeocode : function(input) {
723
			var query = input.value;
724
			var results = [];
725
			geocodeSearch.clearResults(input);
726
			input.className = input.className.replace('is-geocoded', 'not-geocoded');
727
			geocodeSearch.removeMarker(input.id);
728
			if(query.length < 3) return;
729
			geocoder.geocode(query, function(data) {
730
				$.each(data, function(index, cont) {
731
					results.push(cont);
732
				});
733
				var formData = {
734
					name : query
735
				};
736
				$.post(OC.generateUrl('/apps/maps/api/1.0/favorite/getFavoritesByName'), formData, function(data){
737
					$.each(data, function(index, content) {
738
						content.center = L.latLng(content.lat, content.lng);
739
						content.bbox = null;
740
						content.type = 'favorite';
741
						$.each(data, function(index, cont) {
742
							results.push(cont);
743
						});
744
					});
745
				});
746
			}, null);
747
			return results;
748
		},
749
		performGeocode : function(input) {
750
			var query = input.value;
751
			geocodeSearch.clearResults(input);
752
			input.className = input.className.replace('is-geocoded', 'not-geocoded');
753
			geocodeSearch.removeMarker(input.id);
754
			if(query.length < 3) return;
755
			geocoder.geocode(query, function(data) {
756
				geocodeSearch.addResults(data);
757
				var formData = {
758
					name : query
759
				};
760
				$.post(OC.generateUrl('/apps/maps/api/1.0/favorite/getFavoritesByName'), formData, function(data){
761
					$.each(data, function(index, content) {
762
						content.center = L.latLng(content.lat, content.lng);
763
						content.bbox = null;
764
						content.type = 'favorite';
765
						geocodeSearch.addResults(data);
766
					});
767
					geocodeSearch.displayResults(input);
768
				});
769
			}, null);
770
			//TODO search for contacts
771
		},
772
		addResults : function(searchResults) {
773
			$.each(searchResults, function(index, cont) {
774
				geocodeSearch.results.push(cont);
775
			})
0 ignored issues
show
There should be a semicolon.

Requirement of semicolons purely is a coding style issue since JavaScript has specific rules about semicolons which are followed by all browsers.

Further Readings:

Loading history...
776
		},
777
		displayResults : function(input) {
778
			var idParts = input.id.split('-');
779
			var id = idParts[0] + '-results-' + idParts[1];
780
			var list = document.getElementById(id);
781
			$.each(geocodeSearch.results, function(index, content) {
782
				var li = document.createElement('li');
783
				li.appendChild(document.createTextNode(content.name));
784
				li.setAttribute('id', 'entry-' + index);
785
				var className = 'geocoder-list-item';
786
				if(content.type == 'favorite') className += ' icon-starred';
787
				li.setAttribute('class', className);
788
				li.addEventListener('click', function() {
789
					input.className = input.className.replace('not-geocoded', 'is-geocoded');
790
					var marker = L.marker(content.center, {icon: normalMarkerIcon});
791
					geocodeSearch.markers.push([[input.id], [marker]]);
792
					toolKit.addMarker(marker, content.name);
793
					var points = document.getElementsByClassName('is-geocoded');
794
					if(points.length < 2 && content.bbox != null) {
0 ignored issues
show
It is recommended to use !== to compare with null.

Generally, it is recommended to use strict comparison whenever possible and not to rely on the weaker type-juggling comparison operator.

Read more about comparison operations.

Loading history...
795
						map.fitBounds(content.bbox);
796
					};
0 ignored issues
show
This semicolons seems to be unnecessary.
Loading history...
797
					input.value = content.name;
798
					geocodeSearch.clearResults(input);
799
					geocodeSearch.computeRoute();
800
				});
801
				list.appendChild(li);
802
			}, null);
803
			list.style.display = 'block';
804
		},
805
		computeRoute : function() {
806
			geocodeSearch.waypoints = [];
807
			var points = document.getElementsByClassName('is-geocoded');
808
			if(points.length < 2) return;
809
			$.each(points, function(i) {
810
				var id = $(this).attr('id');
811
				var val = $(this).val();
812
				for(var i=0; i<geocodeSearch.markers.length; ++i) {
0 ignored issues
show
It seems like i was already defined.
Loading history...
813
					var curr = geocodeSearch.markers[i];
814
					if(curr[0] == id) {
815
						geocodeSearch.waypoints.push(L.Routing.waypoint(curr[1][0]._latlng, val));
816
						map.removeLayer(curr[1][0]);
817
					}
818
				}
819
			});
820
			routing.setWaypoints(geocodeSearch.waypoints);
821
			routing.on('routeselected', function(e) {
822
				var r = e.route;
823
				var name = r.name;
824
				var time = r.summary.totalTime;
825
				var distance = r.summary.totalDistance;
826
				if(distance >= 1000) distance = parseFloat(distance/1000).toFixed(1) + 'km';
827
				else distance += 'm';
828
				time = Math.ceil(time / 60); // time as minutes
829
				var hours = Math.floor(time / 60);
830
				var minutes = time % 60;
831
				alert('Route ' + name + ' is ' + distance + ' long and takes about ' + hours + ':' + minutes);
832
			});
833
		},
834
		waypointsChanged : function(e) {
835
			var wps = e.waypoints;
836
			var wpscount = wps.length;
837
			var inputcount = document.getElementById('search').childNodes.length;
838
			while(wpscount > inputcount) {
839
				geocodeSearch.addGeocoder();
840
				inputcount++;
841
			}
842
			var inputContainers = document.getElementById('search').childNodes;
843
			$.each(wps, function(idx, wp) {
844
				var name = wp.name;
845
				if(name == null || name == '') {
0 ignored issues
show
It is recommended to use === to compare with null.

Generally, it is recommended to use strict comparison whenever possible and not to rely on the weaker type-juggling comparison operator.

Read more about comparison operations.

Loading history...
It is recommended to use === to compare with .

Generally, it is recommended to use strict comparison whenever possible and not to rely on the weaker type-juggling comparison operator.

Read more about comparison operations.

Loading history...
846
					geocoder.reverse(wp.latLng, 67108864, function(data) {
847
						var input = inputContainers[idx].getElementsByClassName('geocoder')[0];
848
						data = data[0];
849
						input.value = data.name;
850
						wp.name = data.name;
851
						wp.latLng = data.center;
852
					});
853
				}
854
			});
855
		}
856
	},
857
858
	mapSearch = {
859
		searchItems : [],
860
		_ids : [],
861
		getSearchResults : function(data, callback) {
862
			$.getJSON(OC.generateUrl('/apps/maps/search'), data, function renderSearchResults(r) {
863
				callback(r)
0 ignored issues
show
There should be a semicolon.

Requirement of semicolons purely is a coding style issue since JavaScript has specific rules about semicolons which are followed by all browsers.

Further Readings:

Loading history...
864
			})
0 ignored issues
show
There should be a semicolon.

Requirement of semicolons purely is a coding style issue since JavaScript has specific rules about semicolons which are followed by all browsers.

Further Readings:

Loading history...
865
		},
866
867
		showResultsOnMap : function(r) {
868
			if (map.getZoom() <= 14) {
869
				var zoomMSG = '<div class="leaflet-control-minZoomIndecator leaflet-control" style="border-radius: 3px; padding: 3px 7px; display: block; background: rgba(255, 255, 255, 0.701961);">Results might be limited due current zoom, zoom in to get more</div>'
0 ignored issues
show
There should be a semicolon.

Requirement of semicolons purely is a coding style issue since JavaScript has specific rules about semicolons which are followed by all browsers.

Further Readings:

Loading history...
870
				$('.leaflet-top.leaflet-middle').html(zoomMSG);
871
			} else {
872
				$('.leaflet-control-minZoomIndecator ').remove();
873
			}
874
			console.log(r)
0 ignored issues
show
There should be a semicolon.

Requirement of semicolons purely is a coding style issue since JavaScript has specific rules about semicolons which are followed by all browsers.

Further Readings:

Loading history...
875
876
			$.each(r.contacts, function() {
877
				var contact = this;
878
				if ($.inArray(contact.id, mapSearch._ids) != -1) {
879
					return;
880
				}
881
				if (contact.location) {
882
					if (contact.thumbnail) {
883
						var imagePath = contact.thumbnail;
884
						var iconImage = L.icon({
885
							iconUrl : imagePath,
886
							iconSize : [42, 49],
887
							iconAnchor : [21, 49],
888
							popupAnchor : [0, -49],
889
							className : 'contact-icon'
890
						});
891
					} else {
892
						var imagePath = OC.filePath('maps', 'img', 'icons/marker_anonPerson.png');
0 ignored issues
show
It seems like imagePath was already defined.
Loading history...
893
						var iconImage = L.icon({
0 ignored issues
show
It seems like iconImage was already defined.
Loading history...
894
							iconUrl : imagePath,
895
							iconSize : [42, 49],
896
							iconAnchor : [21, 49],
897
							popupAnchor : [0, -49]
898
						});
899
					}
900
901
					for(i=0; i<contact.location.length; i++){
902
						if(contact.location[i] == null) continue;
0 ignored issues
show
It is recommended to use === to compare with null.

Generally, it is recommended to use strict comparison whenever possible and not to rely on the weaker type-juggling comparison operator.

Read more about comparison operations.

Loading history...
903
						var markerHTML = '<h2>' + contact.FN + "</h2>";
904
						var street = [contact.ADR[i][0], contact.ADR[i][1], contact.ADR[i][2]].clean('').join('<br />');
905
						var city = (contact.ADR[i][3]) ? contact.ADR[i][3] : '';
906
						markerHTML += '<br />' + street + ", " + city;
907
						markerHTML += (contact.TEL) ? '<br />Tel: ' + escape(contact.TEL[0]) : '';
908
						var marker = L.marker([contact.location[i].lat * 1, contact.location[i].lon * 1], {
909
							icon : iconImage
910
						});
911
						toolKit.addMarker(marker, markerHTML)
0 ignored issues
show
There should be a semicolon.

Requirement of semicolons purely is a coding style issue since JavaScript has specific rules about semicolons which are followed by all browsers.

Further Readings:

Loading history...
There were too many errors found in this file; checking aborted after 48%.

If JSHint finds too many errors in a file, it aborts checking altogether because it suspects a configuration issue.

Further Reading:

Loading history...
912
						mapSearch.searchItems.push(marker);
913
					}
914
				}
915
				mapSearch._ids.push(contact.id)
916
			})
917
918
			$.each(r.addresses, function() {
919
				if ($.inArray(this.place_id, mapSearch._ids) != -1) {
920
					return;
921
				}
922
				var markerHTML = this.display_name.replace(',', '<br />');
923
				var marker = new L.marker([this.lat * 1, this.lon * 1], {
924
				    icon: normalMarkerIcon}
925
				);
926
				toolKit.addMarker(marker, markerHTML)
927
				mapSearch.searchItems.push(marker);
928
				mapSearch._ids.push(this.place_id)
929
930
			});
931
932
			$.each(r.nodes, function() {
933
				if ($.inArray(this.place_id, mapSearch._ids) != -1) {
934
					return;
935
				}
936
				var iconImage = toolKit.getPoiIcon(this.type)
937
				var markerHTML = this.display_name.replace(',', '<br />');
938
				if (iconImage) {
939
					var marker = L.marker([this.lat * 1, this.lon * 1], {
940
						icon : iconImage
941
					});
942
				} else {
943
					var marker = new L.marker([this.lat * 1, this.lon * 1], {
944
					    icon: normalMarkerIcon
945
					});
946
				}
947
				toolKit.addMarker(marker, markerHTML)
948
				mapSearch.searchItems.push(marker);
949
				mapSearch._ids.push(this.place_id)
950
951
			});
952
953
		},
954
955
		clearSearchResults : function() {
956
			for ( i = 0; i < mapSearch.searchItems.length; i++) {
957
				map.removeLayer(mapSearch.searchItems[i]);
958
			}
959
			mapSearch.searchItems = []
960
		}
961
	}
962
963
	Maps = {
964
		addressbooks : [],
965
		addresses : [],
966
		tempArr : [],
967
		tempCounter : 0,
968
		tempTotal : 0,
969
		activeLayers : [],
970
		poiMarker : [],
971
		overpassLayers : [],
972
		mouseDowntime : 0,
973
		droppedPin : {},
974
		dragging : false,
975
		activeDevices : [],
976
		deviceMarkers : [],
977
		trackMarkers : {},
978
		trackingTimer : {},
979
		deviceTimer : 0,
980
		historyTrack : {},
981
		traceDevice : null,
982
		arrowHead : {},
983
		displayPoiIcons : function(zoomLevel) {
984
			$.each(poiLayers, function(i, layer) {
985
				$.each(layer, function(group, values) {
986
					$.each(values, function(j, value) {
987
						var overpassLayer = new L.OverPassLayer({
988
							minZoom: 9,
989
							query: 'node({{bbox}})[' + group + '=' + value + '];out;',
990
							onSuccess: function(data) {
991
								for ( i = 0; i < data.elements.length; i++) {
992
									e = data.elements[i];
993
									if (e.id in this.instance._ids) {
994
										return;
995
									}
996
									this.instance._ids[e.id] = true;
997
									var pos = new L.LatLng(e.lat, e.lon);
998
									var popup = Maps.getPoiPopupHTML(e.tags).innerHTML;
999
									poiIcon = toolKit.getPoiIcon(value);
1000
									if (poiIcon) {
1001
										var marker = L.marker(pos, {
1002
											icon : poiIcon,
1003
										}).bindPopup(popup);
1004
										Maps.poiMarker.push(marker);
1005
										toolKit.addMarker(marker, popup)
1006
									}
1007
								}
1008
							}
1009
						});
1010
						map.addLayer(overpassLayer);
1011
						Maps.overpassLayers.push(overpassLayer);
1012
					});
1013
				});
1014
			});
1015
		},
1016
		hidePoiIcons : function() {
1017
			$.each(Maps.overpassLayers, function(i, layer) {
1018
				map.removeLayer(layer);
1019
			});
1020
			Maps.overpassLayers = [];
1021
			$.each(Maps.poiMarker, function(i, marker) {
1022
				map.removeLayer(marker);
1023
			});
1024
			Maps.poiMarker = [];
1025
		},
1026
		getPoiPopupHTML : function(tags) {
1027
			var div = document.createElement('div');
1028
1029
			var housenr = '';
1030
			var street = '';
1031
			var city = '';
1032
			var postcode = '';
1033
			var suburb = '';
1034
			if(tags['addr:street']) street = tags['addr:street'];
1035
			else if(tags['road']) street = tags['road'];
1036
			else if(tags['footway']) street = tags['footway'];
1037
			if(tags['addr:housenumber']) housenr = tags['addr:housenumber'];
1038
			else if(tags['house_number']) housenr = tags['house_number'];
1039
			if(tags['addr:postcode']) postcode = tags['addr:postcode'];
1040
			else if(tags['postcode']) postcode = tags['postcode'];
1041
			if(tags['addr:city']) city = tags['addr:city'];
1042
			else if(tags['town']) city = tags['town'];
1043
			else if(tags['city']) city = tags['city'];
1044
			if(tags['suburb']) suburb = tags['suburb'];
1045
			else if(tags['city_district']) suburb = tags['city_district'];
1046
1047
			var title = document.createElement('h2');
1048
			var titleTxt = '';
1049
			if(tags['name']) titleTxt = tags['name'];
1050
			else if(tags['building']) titleTxt = tags['building'];
1051
			//use street as title fallback
1052
			else if(street.length > 0) {
1053
				titleTxt = street;
1054
				if(housenr.length > 0) titleTxt += ' ' + housenr;
1055
			}
1056
			title.appendChild(document.createTextNode(titleTxt));
1057
			div.appendChild(title);
1058
1059
			var addr = '';
1060
			if(street.length > 0) addr += street;
1061
			if(housenr.length > 0) addr += ' ' + housenr;
1062
			if(postcode.length > 0) addr += ', ' + postcode;
1063
			if(city.length > 0) addr += ' ' + city;
1064
			if(suburb.length > 0) addr += ' (' + suburb + ')';
1065
			div.appendChild(document.createTextNode(addr));
1066
			if(tags['phone']) {
1067
				var phoneDiv = document.createElement('div');
1068
				var phone = document.createElement('a');
1069
				var phoneN = tags['phone'];
1070
				if(phoneN.startsWith('+')) phoneN = phoneN.substring(1);
1071
				phone.setAttribute('href', 'tel://' + phoneN);
1072
				phone.appendChild(document.createTextNode(tags['phone']));
1073
				phoneDiv.appendChild(phone);
1074
				div.appendChild(phoneDiv);
1075
			}
1076
			if(tags['website']) {
1077
				var webDiv = document.createElement('div');
1078
				var web = document.createElement('a');
1079
				var webN = tags['website'];
1080
				web.setAttribute('href', webN);
1081
				web.setAttribute('target', '_blank');
1082
				web.appendChild(document.createTextNode(webN));
1083
				webDiv.appendChild(web);
1084
				div.appendChild(webDiv);
1085
			}
1086
			if(tags['email']) {
1087
				var mailDiv = document.createElement('div');
1088
				var mail = document.createElement('a');
1089
				var mailN = tags['email'];
1090
				mail.setAttribute('href', 'mailto:' + mailN);
1091
				mail.appendChild(document.createTextNode(mailN));
1092
				mailDiv.appendChild(mail);
1093
				div.appendChild(mailDiv);
1094
			}
1095
			return div;
1096
		},
1097
		loadAdressBooks : function() {
1098
			if($('#contactMenu').length == 0) return;
1099
			Maps.addressbooks = [];
1100
			$.get(OC.generateUrl('apps/contacts/addressbooks/'), function(r) {
1101
				$.each(r.addressbooks, function() {
1102
					var book = {
1103
						'id' : this.id,
1104
						'backend' : this.backend
1105
					}
1106
					Maps.addressbooks.push(book);
1107
				})
1108
				//Load addresses in array
1109
				$.each(Maps.addressbooks, function(ai) {
1110
					$.get(OC.generateUrl('/apps/contacts/addressbook/' + this.backend + '/' + this.id + '/contacts'), function(r) {
1111
						$.each(r.contacts, function(ci) {
1112
							var name = this.data.FN[0].value;
1113
							var adr = {};
1114
							var orgAdr = {};
1115
							if(this.data.ADR){
1116
								for(var i=0; i< this.data.ADR.length; i++){
1117
									var currAddr = this.data.ADR[i].value;
1118
									//remove empty cells (didn't work with delete or any other function...thus: ugly code
1119
									for (var j=currAddr.length-1; j>=0; j--) {
1120
										if (currAddr[j] === null || currAddr[j] === undefined || currAddr[j] === "") {
1121
											currAddr.splice(j,1);
1122
										}
1123
									}
1124
									adr[i] = currAddr.toString().replace(/,/g, ", ");
1125
									orgAdr[i] = this.data.ADR[i].value;
1126
								}
1127
1128
							}
1129
							if(!adr[0]) return true;
1130
							var addr = {
1131
								'name': name,
1132
								'add': adr,
1133
								'orgAdd': orgAdr
1134
							};
1135
							Maps.addresses.push(addr);
1136
						});
1137
					});
1138
				});
1139
				//end
1140
				Maps.loadContacts();
1141
			})
1142
		},
1143
		removePosistionHistory : function(deviceId) {
1144
			for ( i = 0; i < Maps.trackMarkers[deviceId].length; i++) {
1145
				map.removeLayer(Maps.trackMarkers[deviceId][i]);
1146
			}
1147
			if (Maps.historyTrack[deviceId]) {
1148
				map.removeLayer(Maps.historyTrack[deviceId]);
1149
				map.removeLayer(Maps.arrowHead[deviceId]);
1150
			}
1151
			clearTimeout(Maps.trackingTimer[deviceId]);
1152
		},
1153
1154
		loadDevicePosistionHistory : function(deviceId, trackCurrentPosition, from, till) {
1155
			var trackCurrentPosition = (trackCurrentPosition) ? true : false;
1156
			var from = (from) ? from : '';
1157
			var till = (till) ? till : '';
1158
			var data = {
1159
				devices : deviceId,
1160
				'from' : from,
1161
				'till' : till
1162
			};
1163
1164
			if (!Maps.trackMarkers[deviceId]) {
1165
				Maps.trackMarkers[deviceId] = [];
1166
				Maps.arrowHead[deviceId] = [];
1167
				Maps.historyTrack[deviceId] = [];
1168
				Maps.trackingTimer[deviceId] = [];
1169
			}
1170
1171
			clearTimeout(Maps.trackingTimer);
1172
			$.get(OC.generateUrl('/apps/maps/api/1.0/location/loadLocations'), data, function(response) {
1173
				var locations = response[deviceId];
1174
				var points = [];
1175
				var bounds = []
1176
				var lastPosition = locations[0];
1177
				for ( i = 0; i < Maps.trackMarkers[deviceId].length; i++) {
1178
					map.removeLayer(Maps.trackMarkers[deviceId][i]);
1179
				}
1180
				if (Maps.historyTrack[deviceId]) {
1181
					map.removeLayer(Maps.historyTrack[deviceId]);
1182
					map.removeLayer(Maps.arrowHead[deviceId]);
1183
				}
1184
				if (locations.length === 0) {
1185
					OC.Notification.showTimeout('No results');
1186
					return;
1187
				}
1188
				var lastPObject = L.latLng(lastPosition.lat, lastPosition.lng) //distanceTo
1189
				$.each(locations, function(k, location) {
1190
					var markerHTML = '';
1191
					var marker = new L.marker([location.lat * 1, location.lng * 1]);
1192
					var point = new L.LatLng(location.lat * 1, location.lng * 1);
1193
					//bounds.push(marker.getBounds());
1194
					var markerHTML = location.name + '<br />';
1195
					markerHTML += 'Lat: ' + location.lat + ' Lon: ' + location.lng + '<br />';
1196
					markerHTML += 'Speed: ' + location.speed + '<br />'
1197
					markerHTML += 'Time: ' + new Date(location.timestamp * 1000).toLocaleString("nl-NL")
1198
					var marker = new L.marker([location.lat * 1, location.lng * 1], {
1199
					    icon: normalMarkerIcon
1200
					});
1201
					if(Math.round(lastPObject.distanceTo(point))> 15){ //If markers are more then 15m between each other
1202
						console.log('Distance from last point: ',Math.round(lastPObject.distanceTo(point)));
1203
						Maps.trackMarkers[location.deviceId].push(marker);
1204
						toolKit.addMarker(marker, markerHTML);
1205
						points.push(point);
1206
					}
1207
					lastPObject = point;
1208
				});
1209
				points.reverse();
1210
				Maps.historyTrack[deviceId] = new L.Polyline(points, {
1211
					color : 'green',
1212
					weight : 6,
1213
					opacity : 0.5,
1214
					smoothFactor : 1
1215
1216
				});
1217
				Maps.historyTrack[deviceId].addTo(map);
1218
				Maps.arrowHead[deviceId] = L.polylineDecorator(Maps.historyTrack[deviceId]).addTo(map);
1219
				Maps.arrowHead[deviceId].setPatterns([{
1220
					offset : '0%',
1221
					repeat : 100,
1222
					symbol : L.Symbol.arrowHead({
1223
						pixelSize : 25,
1224
						polygon : false,
1225
						pathOptions : {
1226
							stroke : true
1227
						}
1228
					})
1229
				}]);
1230
				Maps.trackingTimer[deviceId] = setTimeout(function() {
1231
					Maps.loadDevicePosistionHistory(deviceId, trackCurrentPosition, from, till);
1232
				}, 60000)
1233
				if (trackCurrentPosition && lastPosition.deviceId == Maps.traceDevice) {
1234
					map.panTo(new L.LatLng(lastPosition.lat, lastPosition.lng));
1235
				}
1236
			});
1237
		},
1238
		clearDevicePosistionHistory : function(deviceId) {
1239
			for ( i = 0; i < Maps.trackMarkers[deviceId].length; i++) {
1240
				map.removeLayer(Maps.trackMarkers[deviceId][i]);
1241
			}
1242
			map.removeLayer(Maps.historyTrack[deviceId]);
1243
			map.removeLayer(Maps.arrowHead[deviceId]);
1244
			clearTimeout(Maps.trackingTimer[deviceId]);
1245
			Maps.trackMarkers[deviceId] = [];
1246
		},
1247
		loadDevicesLastPosition : function() {
1248
			var data = {
1249
				devices : Maps.activeDevices.join(','),
1250
				limit : 1
1251
			};
1252
			clearTimeout(Maps.deviceTimer);
1253
			if (Maps.activeDevices.length > 0) {
1254
				$.get(OC.generateUrl('/apps/maps/api/1.0/location/loadLocations'), data, function(response) {
1255
					Maps.clearDevicePositions();
1256
					$.each(response, function(deviceId, location) {
1257
						console.log(deviceId, location);
1258
						var device = location[0];
1259
						if (!device)
1260
							return;
1261
1262
						var markerHTML = device.name + '<br />';
1263
						markerHTML += 'Lat: ' + device.lat + ' Lon: ' + device.lng + '<br />';
1264
						markerHTML += 'Speed: ' + device.speed + '<br />'
1265
						markerHTML += 'Time: ' + new Date(device.timestamp * 1000).toLocaleString("nl-NL")
1266
						var marker = new L.marker([device.lat * 1, device.lng * 1], {
1267
						    icon: normalMarkerIcon
1268
						});
1269
						toolKit.addMarker(marker, markerHTML)
1270
						Maps.deviceMarkers.push(marker);
1271
						if (device.deviceId == Maps.traceDevice) {
1272
							map.panTo(new L.LatLng(device.lat * 1, device.lng * 1));
1273
						}
1274
					});
1275
					Maps.deviceTimer = setTimeout(Maps.loadDevicesLastPosition, 60000);
1276
				});
1277
			}
1278
		},
1279
		clearDevicePositions : function() {
1280
			for ( i = 0; i < Maps.deviceMarkers.length; i++) {
1281
				map.removeLayer(Maps.deviceMarkers[i]);
1282
			}
1283
			Maps.deviceMarkers = [];
1284
		},
1285
1286
		loadContacts : function() {
1287
			Maps.tempArr = [];
1288
			Maps.tempTotal = 0;
1289
			Maps.tempCounter = 0;
1290
			$.each(Maps.addressbooks, function(ai) {
1291
				$.get(OC.generateUrl('/apps/contacts/addressbook/' + this.backend + '/' + this.id + '/contacts'), function(r) {
1292
					$.each(r.contacts, function(ci) {
1293
						Maps.tempArr.push(this);
1294
						if (ai == Maps.addressbooks.length - 1 && ci == r.contacts.length - 1) {
1295
							Maps.getContactPositionData();
1296
							Maps.tempTotal = Maps.tempArr.length;
1297
							$('#loadingContacts').show();
1298
						}
1299
					});
1300
				});
1301
			});
1302
		},
1303
		getContactPositionData : function() {
1304
1305
			if (Maps.tempArr.length > 0) {
1306
				temp = Maps.tempArr.pop();
1307
				var contact = toolKit.vcardToObject(temp);
1308
				toolKit.adresLookup(contact.adr, function(d) {
1309
					var curperson = $.extend({}, d, contact);
1310
					try {
1311
						toolKit.addFavContactMarker(curperson);
1312
					} catch(e) {
1313
1314
					}
1315
					var total = Maps.tempTotal;
1316
					var index = Maps.tempCounter
1317
					var percent = Math.round((index / total * 100) * 100) / 100;
1318
					toolKit.setProgress(percent);
1319
					$('#cCounter').text(index + 1 + ' of ' + (total * 1 + 1));
1320
					Maps.tempCounter++;
1321
					if (index == total)
1322
						$('#loadingContacts').hide()
1323
					Maps.getContactPositionData();
1324
				})
1325
			}
1326
		},
1327
1328
		saveCurrentLocation : function(e) {
1329
			var center = map.getBounds().getCenter();
1330
			var location = {
1331
				lat : center.lat,
1332
				lng : center.lng
1333
			};
1334
			$.jStorage.set('location', location)
1335
			$.jStorage.set('zoom', e.target._zoom)
1336
		},
1337
1338
		showContact : function(data) {
1339
1340
		}
1341
	}
1342
	toolKit = {
1343
		addMarker : function(marker, markerHTML, openPopup, fav) {
1344
			if(marker == null || marker._latlng == null) return;
1345
			fav = fav || false;
1346
			var openPopup = (openPopup) ? true : false;
1347
			var latlng = marker._latlng.lat + ',' + marker._latlng.lng;
1348
			var markerHTML2 = '<div class="';
1349
			if(fav) {
1350
				markerHTML2 += 'icon-starred removeFromFav" fav-id="' + marker.options.id + '"';
1351
				var editButton = document.createElement('button');
1352
				editButton.className = 'icon-rename editFav';
1353
				var titleEnd = markerHTML.indexOf('</h2>') + 5;
1354
				markerHTML = markerHTML.slice(0, titleEnd) + editButton.outerHTML + markerHTML.slice(titleEnd);
1355
			} else {
1356
				markerHTML2 += 'icon-star addToFav"';
1357
			}
1358
			markerHTML2 += ' data-latlng="' + latlng + '" style="float: left;"></div><div class="marker-popup-content">' + markerHTML + '</div><div><a class="setDestination" data-latlng="' + latlng + '">Navigate here</a></div>';
1359
			marker.addTo(map).bindPopup(markerHTML2);
1360
			marker.on('mouseover', function (e) {
1361
				var tgt = e.originalEvent.fromElement || e.originalEvent.relatedTarget;
1362
				var parent = this._getParent(tgt, 'leaflet-popup');
1363
				if(parent == marker._popup._container) return true;
1364
				marker.openPopup();
1365
			}, this);
1366
			marker.on('mouseout', function (e) {
1367
				var tgt = e.originalEvent.toElement || e.originalEvent.relatedTarget;
1368
				if(this._getParent(tgt, 'leaflet-popup')) {
1369
					L.DomEvent.on(marker._popup._container, 'mouseout', this._popupMouseOut, this);
1370
					return true;
1371
				}
1372
				marker.closePopup();
1373
			}, this);
1374
			if (openPopup === true) {
1375
				setTimeout(function() {
1376
					//L.popup().setLatLng([marker._latlng.lat, marker._latlng.lng]).setContent("I am a standalone popup.").openOn(map);
1377
					marker.openPopup();
1378
				}, 50);
1379
			}
1380
		},
1381
		_getParent: function(element, className) {
1382
			var parent = element.parentNode;
1383
			while (parent != null) {
1384
				if (parent.className && L.DomUtil.hasClass(parent, className))
1385
					return parent;
1386
				parent = parent.parentNode;
1387
			}
1388
			return false;
1389
		},
1390
		_popupMouseOut: function(e) {
1391
			if(marker == null || marker._popup == null) return false;
1392
			L.DomEvent.off(marker._popup, 'mouseout', this._popupMouseOut, this);
1393
			var tgt = e.toElement || e.relatedTarget;
1394
			if (this._getParent(tgt, 'leaflet-popup')) return true;
1395
			if (tgt == this._icon) return true;
1396
			marker.closePopup();
1397
		},
1398
		getPoiIcon : function(icon) {
1399
			return toolKit.toMakiMarkerIcon(icon.replace(' ', ''));
1400
		},
1401
1402
		toMakiMarkerIcon : function(type) {
1403
			var mapper = {
1404
				'aerialway' : [ 'aerialway' ],
1405
				'airfield' : [ 'airfield' ],
1406
				'airport' : [ 'airport' ],
1407
				'alcohol-shop' : [ 'alcohol-shop', 'alcohol', 'beverages' ],
1408
				'america-football' : [ 'america-football' ],
1409
				'art-gallery' : [ 'art-gallery', 'gallery', 'artwork', 'paint' ],
1410
				'bakery' : [ 'bakery' ],
1411
				'bank' : [ 'bank', 'atm' ],
1412
				'bar' : [ 'bar' ],
1413
				'baseball' : [ 'baseball' ],
1414
				'basketball' : [ 'basketball' ],
1415
				'beer' : [ 'beer', , 'biergarten', 'pub' ],
1416
				'bicycle' : [ 'bicycle', 'bicycle_parking' ],
1417
				'building' : [ 'building' ],
1418
				'bus' : [ 'bus', 'bus_stop', 'bus_station'],
1419
				'cafe' : [ 'cafe' ],
1420
				'camera' : [ 'camera', 'viewpoint' ],
1421
				'campsite' : [ 'campsite', 'camp_site', 'caravan_site' ],
1422
				'car' : [ 'car' ],
1423
				'cemetery' : [ 'cemetery' ],
1424
				'chemist' : [ 'chemist' ],
1425
				'cinema' : [ 'cinema' ],
1426
				'circle' : [ 'circle' ],
1427
				'circle-stroked' : [ 'circle-stroked' ],
1428
				'city' : [ 'city' ],
1429
				'clothing-store' : [ 'clothing-store', 'clothes' ],
1430
				'college' : [ 'college' ],
1431
				'commercial' : [ 'commercial' ],
1432
				'cricket' : [ 'cricket' ],
1433
				'cross' : [ 'cross' ],
1434
				'dam' : [ 'dam' ],
1435
				'danger' : [ 'danger' ],
1436
				'dentist' : [ 'dentist' ],
1437
				'disability' : [ 'disability' ],
1438
				'dog-park' : [ 'dog-park' ],
1439
				'embassy' : [ 'embassy' ],
1440
				'emergency-telephone' : [ 'emergency-telephone' ],
1441
				'entrance' : [ 'entrance' ],
1442
				'farm' : [ 'farm' ],
1443
				'fast-food' : [ 'fast-food' ],
1444
				'ferry' : [ 'ferry' ],
1445
				'fire-station' : [ 'fire-station', 'fire_station' ],
1446
				'fuel' : [ 'fuel' ],
1447
				'garden' : [ 'garden' ],
1448
				'gift' : [ 'gift' ],
1449
				'golf' : [ 'golf' ],
1450
				'grocery' : [ 'grocery', 'supermarket', 'deli', 'convenience', 'greengrocer', 'fishmonger', 'butcher', 'marketplace' ],
1451
				'hairdresser' : [ 'hairdresser' ],
1452
				'harbor' : [ 'harbor' ],
1453
				'heart' : [ 'heart' ],
1454
				'heliport' : [ 'heliport' ],
1455
				'hospital' : [ 'hospital' ],
1456
				'ice-cream' : [ 'ice-cream' ],
1457
				'industrial' : [ 'industrial' ],
1458
				'land-use' : [ 'land-use' ],
1459
				'laundry' : [ 'laundry' ],
1460
				'library' : [ 'library', 'books' ],
1461
				'lighthouse' : [ 'lighthouse' ],
1462
				'lodging' : [ 'lodging', 'hotel', 'guest_house', 'hostel', 'motel' ],
1463
				'logging' : [ 'logging' ],
1464
				'london-underground' : [ 'london-underground' ],
1465
				'marker' : [ 'marker' ],
1466
				'marker-stroked' : [ 'marker-stroked' ],
1467
				'minefield' : [ 'minefield' ],
1468
				'mobilephone' : [ 'mobilephone', 'mobile_phone', 'gsm' ],
1469
				'monument' : [ 'monument' ],
1470
				'museum' : [ 'museum' ],
1471
				'music' : [ 'music' ],
1472
				'oil-well' : [ 'oil-well' ],
1473
				'park' : [ 'park' ],
1474
				'parking' : [ 'parking' ],
1475
				'parking-garage' : [ 'parking-garage', 'parking-entrance' ],
1476
				'pharmacy' : [ 'pharmacy', 'drugstore' ],
1477
				'pitch' : [ 'pitch', 'sports' ],
1478
				'place-of-worship' : [ 'place-of-worship', 'place_of_worship' ],
1479
				'playground' : [ 'playground' ],
1480
				'police' : [ 'police' ],
1481
				'polling-place' : [ 'polling-place' ],
1482
				'post' : [ 'post', 'post_box', 'post_office' ],
1483
				'prison' : [ 'prison' ],
1484
				'rail' : [ 'rail' ],
1485
				'rail-above' : [ 'rail-above' ],
1486
				'rail-light' : [ 'rail-light' ],
1487
				'rail-metro' : [ 'rail-metro' ],
1488
				'rail-underground' : [ 'rail-underground' ],
1489
				'religious-christian' : [ 'religious-christian' ],
1490
				'religious-jewish' : [ 'religious-jewish' ],
1491
				'religious-muslim' : [ 'religious-muslim' ],
1492
				'restaurant' : [ 'restaurant' ],
1493
				'roadblock' : [ 'roadblock' ],
1494
				'rocket' : [ 'rocket' ],
1495
				'school' : [ 'school', 'kindergarten' ],
1496
				'scooter' : [ 'scooter' ],
1497
				'shop' : [ 'shop', 'general', 'bag', 'furniture', 'variety_store', 'houseware', 'department_store', 'florist', 'outdoor', 'kiosk', 'kitchen', 'shoes', 'jewelry', 'sports' ],
1498
				'skiing' : [ 'skiing' ],
1499
				'slaughterhouse' : [ 'slaughterhouse' ],
1500
				'soccer' : [ 'soccer' ],
1501
				'square' : [ 'square' ],
1502
				'square-stroked' : [ 'square-stroked' ],
1503
				'star' : [ 'star' ],
1504
				'star-stroked' : [ 'star-stroked' ],
1505
				'suitcase' : [ 'suitcase' ],
1506
				'swimming' : [ 'swimming', 'swimming_pool' ],
1507
				'telephone' : [ 'telephone', 'phone' ],
1508
				'tennis' : [ 'tennis' ],
1509
				'theatre' : [ 'theatre' ],
1510
				'toilets' : [ 'toilets' ],
1511
				'town' : [ 'town' ],
1512
				'town-hall' : [ 'town-hall', 'townhall' ],
1513
				'triangle' : [ 'triangle' ],
1514
				'triangle-stroked' : [ 'triangle-stroked' ],
1515
				'village' : [ 'village' ],
1516
				'warehouse' : [ 'warehouse' ],
1517
				'waste-basket' : [ 'waste-basket' ],
1518
				'water' : [ 'water' ],
1519
				'wetland' : [ 'wetland' ],
1520
				'zoo' : [ 'zoo' ]
1521
			}
1522
			var iconUrl = OC.filePath('maps', 'vendor', 'maki/src/');
1523
			var iconSuffix = '-18.svg';
1524
			var name;
1525
			var icon;
1526
			$.each(mapper, function(iconName, types) {
1527
				if (types.indexOf(type) > -1) {
1528
					name = iconName;
1529
					icon = iconUrl + iconName + iconSuffix;
1530
					return false;
1531
				}
1532
			})
1533
			return L.icon({
1534
					className: type + ' maki-icon',
1535
					icon: name,
1536
					iconUrl : icon,
1537
					iconSize : [18, 18],
1538
					iconAnchor : [9, 18],
1539
					popupAnchor : [0, -18]
1540
			});
1541
		},
1542
		toFAClass : function(type) {
1543
			var mapper = {
1544
				'shopping-cart' : ['supermarkt', 'supermarket', 'department_store', 'deli'],
1545
				'medkit' : ['hospital'],
1546
				'cutlery' : ['fast_food', 'restaurant'],
1547
				'beer' : ['pub'],
1548
				'credit-card' : ['atm'],
1549
				'graduation-cap' : ['school'],
1550
				'lightbulb-o' : ['electronics'],
1551
				'cut' : ['hairdresser'],
1552
				'info' : ['information'],
1553
				'refresh' : ['recycling'],
1554
				'asterisk' : ['attraction', 'theme_park'],
1555
				'cogs' : ['car_repair'],
1556
				'wrench' : ['doityourself'],
1557
				'music' : ['hifi'],
1558
				'gift' : ['gift'],
1559
				'globe' : ['travel_agency'],
1560
				'minus' : ['bench', 'picnic_site'],
1561
				'desktop' : ['computer'],
1562
				'eye' : ['optician'],
1563
				'cubes' : ['toys'],
1564
				'file-picture' : ['photo'],
1565
				'copy' : ['copyshop'],
1566
				'paw' : ['pet'],
1567
				'clock-o' : ['clock'],
1568
				'key' : ['locksmith'],
1569
				'video-camera' : ['video'],
1570
				'magic' : ['party'],
1571
				'road' : ['living_street'],
1572
				'home' : ['residential']
1573
			}
1574
			var returnClass = false;
1575
			$.each(mapper, function(faClass, types) {
1576
				if (types.toString().indexOf(type) > -1) {
1577
					returnClass = faClass
1578
				} else {
1579
				}
1580
			})
1581
			if (returnClass == false)
1582
				console.log('Type icon not found: ' + type)
1583
			return returnClass;
1584
		},
1585
		vcardToObject : function(vcard) {
1586
			var contact = {};
1587
1588
			$.each(vcard.data, function(k, v) {
1589
				if(vcard.data.photo == true){
1590
					var uid = vcard.metadata['id'];
1591
					var parent = vcard.metadata['parent'];
1592
					var backend = vcard.metadata['backend'];
1593
					contact.thumbnail = 'apps/contacts/addressbook/' + backend + '/' + parent + '/contact/' + uid + '/photo';
1594
					contact.thumbnail = OC.generateUrl(contact.thumbnail);
1595
				}
1596
				if (v[0]) {
1597
					if ($.isArray(v[0]['value'])) {
1598
						if (k === 'ADR') {
1599
							var adr = {
1600
								street : v[0]['value'][0] + v[0]['value'][1] + v[0]['value'][2],
1601
								city : v[0]['value'][3],
1602
								postalCode : v[0]['value'][5],
1603
								country : v[0]['value'][6]
1604
							}
1605
							contact[k.toLowerCase()] = adr;
1606
						} else {
1607
							contact[k.toLowerCase()] = v[0]['value'].clean('').join(',');
1608
						}
1609
					} else {
1610
						contact[k.toLowerCase()] = v[0]['value'];
1611
					}
1612
				} else {
1613
					console.log(k, v)
1614
				}
1615
			})
1616
			return contact;
1617
		},
1618
		/**
1619
		 *
1620
		 * @param {Object} street+houseno,City,Country
1621
		 * @param {Function} callback func
1622
		 */
1623
		adresLookup : function(address, callback) {
1624
1625
			if(address !== undefined){
1626
				var getData = {
1627
					street : address.street,
1628
					city : address.city,
1629
					country : address.country
1630
				}
1631
			} else {
1632
				var getData = null;
1633
			}
1634
			$.getJSON(OC.generateUrl('/apps/maps/adresslookup'), getData, function(r) {
1635
				callback(r)
1636
			})
1637
		},
1638
		currentMarker : null,
1639
		favMarkers : [],
1640
		addFavContactMarker : function(contact) {
1641
			if (contact.thumbnail) {
1642
				var imagePath = contact.thumbnail
1643
				var iconImage = L.icon({
1644
					iconUrl : imagePath,
1645
					iconSize : [42, 49],
1646
					iconAnchor : [21, 49],
1647
					popupAnchor : [0, -49],
1648
					className : 'contact-icon'
1649
				});
1650
			} else {
1651
				var imagePath = OC.filePath('maps', 'img', 'icons/marker_anonPerson.png');
1652
				var iconImage = L.icon({
1653
					iconUrl : imagePath,
1654
					iconSize : [42, 49],
1655
					iconAnchor : [21, 49],
1656
					popupAnchor : [0, -49]
1657
				});
1658
			}
1659
1660
			var markerHTML = '<h2>' + contact.fn + "</h2>";
1661
			var street = (contact.adr.street) ? contact.adr.street : '';
1662
			var city = (contact.adr.city) ? contact.adr.city : '';
1663
			markerHTML += '<br />' + street + " " + city;
1664
			markerHTML += (contact.tel) ? '<br />Tel: ' + escape(contact.tel) : '';
1665
			var marker = L.marker([contact.lat * 1, contact.lon * 1], {
1666
				icon : iconImage
1667
			});
1668
			toolKit.addMarker(marker, markerHTML)
1669
			toolKit.favMarkers.push(marker);
1670
		},
1671
		removeFavMarkers : function() {
1672
			for ( i = 0; i < toolKit.favMarkers.length; i++) {
1673
				map.removeLayer(toolKit.favMarkers[i]);
1674
			}
1675
			toolKit.favMarkers = []
1676
		},
1677
1678
		setProgress : function(percent) {
1679
			var $element = $('#progressBar');
1680
			var progressBarWidth = percent * $element.width() / 100;
1681
			$element.find('div').animate({
1682
				width : progressBarWidth
1683
			}, 50);
1684
		}
1685
	}
1686
1687
	poiLayers = {
1688
		9 : {
1689
			shop : [
1690
				'supermarket', 'bicycle'
1691
			],
1692
			amenity : [
1693
				'atm', 'coffee_shop', 'restaurant', 'swimming_pool', 'library', 'toilets', 'post_box', 'bar'
1694
			],
1695
			tourism : [
1696
				'hotel'
1697
			]
1698
		},
1699
		10 : {},
1700
	 	11: {},
1701
		12 : {},
1702
		13 : {},
1703
		14 : {},
1704
		15 : {}
1705
	},
1706
1707
	poiTypes = {
1708
		shop : ['atm', 'coffee_shop', 'restaurant', 'supermarket', 'bicycle', 'hotel', 'swimming_pool', 'library', 'toilets', 'post_box', 'bar'],
1709
		//TODO: define proper categories:
1710
		// Restaurants/food/coffeeshops/bars, Hotels/hostels, Touristic sites, Wifi, Transport, Fuel, Parking, Supermarket, ATM/Bank, Entertainment, Hospital/Pharmacy, Police, Toilets, Post
1711
		//shop : ['supermarket', 'bakery', 'car', 'stationery', 'hairdresser', 'mobile_phone', 'convenience', 'newsagent', 'kiosk', 'computer', 'clothes', 'variety_store', 'hearing_aids', 'florist', 'handicraft', 'candle', 'antique', 'pet', 'massage', 'electronics', 'laundry', 'doityourself', 'sports', 'jewelry', 'musical_instrument', 'chemist', 'shoes', 'beverages', 'toys', 'fishing', 'copyshop', 'beauty', 'bag', 'paint', 'bicycle', 'communication', 'furniture', 'alcohol', 'deli', 'optician', 'books', 'car_repair', 'butcher', 'outdoor', 'motorcycle', 'estate_agent', 'photo', 'gift', 'travel_agency', 'tea', 'wine', 'medical_supply', 'department_store', 'dry_cleaning', 'video', 'second_hand', 'greengrocer', 'erotic', 'curtain', 'haberdashery', 'garden_centre', 'art', 'fashion', 'bags', 'accessoires', 'confectionery', 'ice_cream', 'organic', 'music', 'boutique', 'interior', 'kitchen', 'vacant', 'tattoo', 'mall', 'camera', 'gallery', 'rc_models', 'coffee', 'bicycle_rental', 'photographer', 'ticket', 'charity', 'Shisha', 'hats', 'funeral_directors', 'locksmith', 'fabric', 'hardware', 'shoe_repair', 'hifi', 'fabrics', 'tailor', 'anime', 'market', 'grocery', 'no', 'surf', 'tobacco', 'animals', 'currency_exchange', 'souvenirs', 'internet-tele-cafe', 'photography', 'car_parts', 'antiques', 'bed', 'skating', 'ceramics', 'internet cafe', 'frame', 'brushes', 'fish', 'callshop', 'glass', 'comics', 'pottery', 'internet_cafe', 'stamps', 'radiotechnics', 'interior_decoration', 'carrental', 'interior_design', 'gramophone', 'Trödel', 'unused', 'watches', 'jewellery', 'tatoo', 'travelling', 'telecommunication', 'cigarettes', 'sports food', 'perfumery', 'unknown', 'orthopedics', 'fire_extinguisher', 'fishmonger', 'wholesale', 'lights', 'carpet', 'office_supplies', 'parquet', 'porcelain', 'lamps', 'make-up', 'art_gallery', 'telecom', 'underwear', 'watch', 'tableware', 'scuba_diving', 'christmas', 'tanning', 'craft', 'leather', 'for rent', 'glaziery', 'seafood', 'Sicherheitstechnik', 'coffee machines', 'alteration', 'decoration', 'sport_bet', 'seefood', 'mobile phone service', 'window_blind', 'tyres', 'cheese', 'medical', 'sewing-machine', 'Kaugummi-Automaten', 'Kaugummi-Automat', 'baby', 'games', 'piercing', 'Elektrohaushaltsgeräte', 'electrician', 'glasses', 'circus', 'food', 'marine', 'lottery', 'Hockey', 'electric', 'coins', 'metal workshop', 'nails', 'general', 'tanning_salon', 'crafts', 'household', 'floor', 'baby_goods', 'Patissier', 'delicatessen', 'telephone', 'Hema', 'soft_drugs', 'board_games', 'lingerie', 'candy', 'cd', 'stones', 'spiritual', 'health', 'juice', 'hemp_products', 'smartshop', 'cannabis', 'frozen_yoghurt', 'art_supplies', 'cigar', 'department', 'sok_shop', 'realestate', 'lighting', 'generic', 'nail', 'ink', 'traiteur', 'toko', 'key', 'gsm', 'artist', 'hearth', 'framing', 'espresso_machine', 'knives', 'rental', 'thrift_store', 'snacks', 'tobacconist', 'disused:butcher', 'party', 'audiologist', 'housewares', 'Fashion', 'printing', 'chandler', 'Shoes', 'Electronics', 'softdrugs', 'houseware', 'textiles', 'perfume'],
1712
		//amenity : ["post_box", "police", "atm", "recycling", "parking", "fuel", "telephone", "school", "pub", "doctors", "arts_centre", "cafe", "fast_food", "restaurant", "place_of_worship", "bank", "bicycle_parking", "drinking_water", "theatre", "bar", "bench", "waste_disposal", "nightclub", "pharmacy", "bicycle_rental", "post_office", "charging_station", "waste_basket", "vending_machine", "kindergarten", "marketplace", "dentist", "ev_charging", "bureau_de_change", "library", "cinema", "toilets", "car_wash", "fountain", "boat_rental", "taxi", "bus_parking", "public_building", "driving_school", "physical therapy", "coffee_shop", "embassy", "vacant", "coffeeshop", "ice_cream", "car_rental", "swimming_pool", "university", "casino", "community_centre", "lost_found", "grit_bin", "clock", "parking_entrance", "sauna", "brothel", "ferry_terminal", "fitness_center", "bus_station", "college", "fire_station", "health_centre", "townhall", "hospital", "veterinary", "gym", "fablab", "money_transfer", "kitchen_studio", "tanning_salon", "tanning", "studio"],
1713
		//tourism : ["artwork", "hostel", "attraction", "hotel", "information", "museum", "gallery", "viewpoint", "picnic_site", "guest_house", "theme_park", "apartment", "zoo", "camp_site", "chalet", "motel", "citytour", "aquarium"]
1714
	}
1715
1716
	mapSettings = {
1717
		openTrackingSettings : function() {
1718
			$.get(OC.generateUrl('/apps/maps/api/1.0/location/loadDevices'), function(d) {
1719
				var tableHTML;
1720
				$.each(d, function() {
1721
					tableHTML += '<tr><td>' + this.name + '</td><td>' + this.hash + '</td><td><div class="icon-delete icon" data-deviceId="' + this.id + '"></div></td></tr>';
1722
				});
1723
				$('#trackingDevices tbody').html(tableHTML);
1724
			})
1725
			$('#trackingSettingsPopup').dialog({
1726
				buttons : {
1727
					"Close" : function() {
1728
						$(this).dialog('destroy');
1729
						$('#addtracking')[0].reset()
1730
					}
1731
				}
1732
1733
			});
1734
		},
1735
		saveDevice : function() {
1736
			if ($('#addtracking input').val() == '')
1737
				return
1738
			var formData = {
1739
				name : $('#addtracking input').val()
1740
			};
1741
			$.post(OC.generateUrl('/apps/maps/api/1.0/location/adddevice'), formData, function(d) {
1742
				$('#trackingDevices tbody').append('<tr><td>' + formData.name + '</td><td>' + d.hash + '</td><td><div class="icon-delete icon" data-deviceId="' + d.id + '"></div></td></tr>');
1743
				$('#addtracking input').val('');
1744
			});
1745
		},
1746
1747
		deleteDevice : function(e) {
1748
			var dId = $(this).attr('data-deviceId');
1749
			$(this).parent().parent().remove();
1750
			///api/1.0/location/removedevice
1751
			var formData = {
1752
				deviceId : dId
1753
			};
1754
			$.post(OC.generateUrl('/apps/maps/api/1.0/location/removedevice'), formData);
1755
1756
		}
1757
	}
1758
1759
	favorites = {
1760
		favArray : [],
1761
		add : function(){
1762
			var latlng = $(this).attr('data-latlng').split(',');
1763
			var popup = document.getElementsByClassName('leaflet-popup-content')[0];
1764
			var favicon = popup.getElementsByTagName('div')[0];
1765
			var content = popup.getElementsByTagName('div')[1];
1766
1767
			var popupText = content.innerHTML;
1768
			var orgTitle = content.getElementsByTagName('h2')[0].innerHTML;
1769
			var titleLength = '<h2>' + orgTitle + '</h2>'.length;
1770
			content.innerHTML = popupText.substring(titleLength);
1771
			var formData = {
1772
				lat : latlng[0],
1773
				lng : latlng[1],
1774
				name : orgTitle
1775
			};
1776
			$.post(OC.generateUrl('/apps/maps/api/1.0/favorite/addToFavorites'), formData, function(data) {
1777
				var markerHTML = '<h2>' + formData.name + '</h2>';
1778
				var marker = L.marker([formData.lat, formData.lng], {
1779
								icon : favMarkerIcon,
1780
								id : data.id
1781
				});
1782
				map.removeLayer(currentMarker);
1783
				currentMarker = null;
1784
				toolKit.addMarker(marker, markerHTML, true, true);
1785
				favorites.favArray.push(marker);
1786
			});
1787
		},
1788
		show : function(){
1789
			$.post(OC.generateUrl('/apps/maps/api/1.0/favorite/getFavorites'), null, function(data){
1790
				for(var i=0; i<data.length; i++){
1791
					var fav = data[i];
1792
1793
					var markerHTML = '<h2>' + fav.name + '</h2>';
1794
					var marker = L.marker([fav.lat, fav.lng], {
1795
									icon : favMarkerIcon,
1796
									id : fav.id
1797
					});
1798
					toolKit.addMarker(marker, markerHTML, false, true);
1799
					favorites.favArray.push(marker);
1800
				}
1801
			});
1802
		},
1803
		hide : function(){
1804
			for(var i=0; i<favorites.favArray.length; i++){
1805
				map.removeLayer(favorites.favArray[i]);
1806
			}
1807
			favorites.favArray = [];
1808
		},
1809
		remove : function(){
1810
			var id = $(this).attr('fav-id');
1811
			var formData = {
1812
				id : id
1813
			};
1814
			$.post(OC.generateUrl('/apps/maps/api/1.0/favorite/removeFromFavorites'), formData, function(data){
1815
				for(i=0; i<favorites.favArray.length; ++i) {
1816
					if(favorites.favArray[i].options.id == id) {
1817
						//TODO this code toggles the star icon and the add/remove methods. Should be used as soon as the marker replacement works
1818
						var popup = document.getElementsByClassName('leaflet-popup-content')[0];
1819
						var favicon = popup.getElementsByTagName('div')[0];
1820
						var newClass = favicon.className.replace('icon-starred', 'icon-star');
1821
						favicon.className = newClass.replace('removeFromFav', 'addToFav');
1822
						var removedFav = favorites.favArray.splice(i,1)[0];
1823
						addGeocodeMarker(removedFav.getLatLng());
1824
						map.removeLayer(removedFav);
1825
						return;
1826
					}
1827
				}
1828
			});
1829
		},
1830
		edit : function() {
1831
			var popup = document.getElementsByClassName('leaflet-popup-content')[0];
1832
			var favicon = popup.getElementsByTagName('div')[0];
1833
			var id = favicon.getAttributeNode('fav-id').value;
1834
			var content = popup.getElementsByTagName('div')[1];
1835
			var titleNode = content.getElementsByTagName('h2')[0];
1836
			var title = titleNode.innerHTML;
1837
			var button = content.getElementsByClassName('editFav')[0];
1838
			var input = document.createElement('input');
1839
			input.type = 'text';
1840
			input.value = title;
1841
			titleNode.parentNode.replaceChild(input, titleNode);
1842
			button.className = 'confirmFavEdit icon-checkmark';
1843
			$(document).on('click', '.confirmFavEdit', function() {
1844
				button.className = 'icon-rename editFav';
1845
				if(title != input.value) title = input.value;
1846
				titleNode.innerHTML = title;
1847
				content.replaceChild(titleNode, input);
1848
				var formData = {
1849
					name : title,
1850
					id : id
1851
				};
1852
				$.post(OC.generateUrl('/apps/maps/api/1.0/favorite/updateFavorite'), formData, function(data){
1853
				});
1854
				$(document).off('click', '.confirmFavEdit');
1855
			});
1856
		}
1857
	}
1858
1859
	$(document).on('click', '#tracking-settings', mapSettings.openTrackingSettings);
1860
	$(document).on('click', '#addtracking button', mapSettings.saveDevice);
1861
	$(document).on('click', '#trackingDevices .icon-delete', mapSettings.deleteDevice);
1862
	$(document).on('click', '.addToFav', favorites.add);
1863
	$(document).on('click', '.removeFromFav', favorites.remove);
1864
	$(document).on('click', '.editFav', favorites.edit)
1865
1866
	/**
1867
	 * Extend the OC.Notification object with our own methods
1868
	 */
1869
	OC.Notification.showTimeout = function(text, timeout, isHTML) {
1870
		isHTML = (!isHTML) ? false : true;
1871
		OC.Notification.hide();
1872
		if (OC.Notification.notificationTimer) {
1873
			clearTimeout(notificationTimer);
1874
		}
1875
		timeout = (!timeout) ? 3000 : timeout;
1876
		if (isHTML) {
1877
			OC.Notification.showHtml(text);
1878
		} else {
1879
			OC.Notification.show(text);
1880
		}
1881
		OC.Notification.notificationTimer = setTimeout(function() {
1882
			OC.Notification.hide();
1883
		}, timeout);
1884
	}
1885
})(jQuery, OC);
1886