Failed Conditions
Pull Request — master (#356)
by
unknown
02:26
created

core/js/pokemon.maps.js   F

Complexity

Total Complexity 73
Complexity/F 1.7

Size

Lines of Code 442
Function Count 43

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 5
Bugs 0 Features 0
Metric Value
cc 0
c 5
b 0
f 0
nc 1
dl 0
loc 442
rs 3.9761
wmc 73
mnd 3
bc 69
fnc 43
bpm 1.6046
cpm 1.6976
noi 0

20 Functions

Rating   Name   Duplication   Size   Complexity  
B pokemon.maps.js ➔ createHeatmap 0 28 1
A pokemon.maps.js ➔ showHeatmap 0 5 1
B pokemon.maps.js ➔ initLive 0 28 2
A pokemon.maps.js ➔ initHeatmapData 0 58 2
A pokemon.maps.js ➔ hideLive 0 5 1
A pokemon.maps.js ➔ initHeatmap 0 13 1
A pokemon.maps.js ➔ hideHeatmap 0 4 1
A pokemon.maps.js ➔ isTouchDevice 0 4 1
A pokemon.maps.js ➔ removePokemonMarkerByIv 0 11 4
A pokemon.maps.js ➔ extractEncountersId 0 7 2
B pokemon.maps.js ➔ addPokemonMarker 0 81 6
A pokemon.maps.js ➔ initMap 0 73 1
A pokemon.maps.js ➔ removePokemonMarker 0 9 3
A pokemon.maps.js ➔ isMobileDevice 0 4 1
A pokemon.maps.js ➔ initSelector 0 15 1
A pokemon.maps.js ➔ updateLive 0 20 1
B pokemon.maps.js ➔ updateHeatmap 0 25 1
A pokemon.maps.js ➔ showLive 0 6 1
A pokemon.maps.js ➔ clearPokemonMarkers 0 6 2
A pokemon.maps.js ➔ getIvColor 0 13 4

How to fix   Complexity   

Complexity

Complex classes like core/js/pokemon.maps.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/** global: google */
2
/** global: pokemon_id */
3
/** global: navigator */
4
var map, heatmap;
5
var pokemonMarkers = [];
6
var updateLiveTimeout;
7
8
var ivMin = 80;
9
var ivMax = 100;
10
11
function initMap() {
12
	$.getJSON('core/json/variables.json', function(variables) {
13
		var latitude = Number(variables['system']['map_center_lat']);
14
		var longitude = Number(variables['system']['map_center_long']);
15
		var zoom_level = Number(variables['system']['zoom_level']);
16
		var pokeimg_suffix = variables['system']['pokeimg_suffix'];
17
		var show_encounter_stats = variables['system']['live_show_encounter_stats'];
18
19
		if (show_encounter_stats) {
20
	        	ivMin = 0;
21
                }
22
            
23
		map = new google.maps.Map(document.getElementById('map'), {
24
			center: {
25
				lat: latitude,
26
				lng: longitude
27
			},
28
			zoom: zoom_level,
29
			zoomControl: true,
30
			scaleControl: false,
31
			scrollwheel: true,
32
			disableDoubleClickZoom: false,
33
			streetViewControl: false,
34
			mapTypeControlOptions: {
35
				mapTypeIds: [
36
					google.maps.MapTypeId.ROADMAP,
37
					'pogo_style',
38
					'dark_style',
39
				]
40
			}
41
		});
42
43
		$.getJSON('core/json/pogostyle.json', function(data) {
44
			var styledMap_pogo = new google.maps.StyledMapType(data, { name: 'PoGo' });
45
			map.mapTypes.set('pogo_style', styledMap_pogo);
46
		});
47
		$.getJSON('core/json/darkstyle.json', function(data) {
48
			var styledMap_dark = new google.maps.StyledMapType(data, { name: 'Dark' });
49
			map.mapTypes.set('dark_style', styledMap_dark);
50
		});
51
		$.getJSON('core/json/defaultstyle.json', function(data) {
52
			map.set('styles', data);
53
		});
54
55
		$.ajax({
56
			'type': 'GET',
57
			'global': false,
58
			'dataType': 'json',
59
			'url': 'core/process/aru.php',
60
			'data': {
61
				'type': 'maps_localization_coordinates'
62
			}
63
		}).done(function(coordinates) {
64
			if (navigator.geolocation) {
65
				navigator.geolocation.getCurrentPosition(function(position) {
66
					var pos = {
67
						lat: position.coords.latitude,
68
						lng: position.coords.longitude
69
					};
70
71
					if (position.coords.latitude <= coordinates.max_latitude && position.coords.latitude >= coordinates.min_latitude) {
72
						if (position.coords.longitude <= coordinates.max_longitude && position.coords.longitude >= coordinates.min_longitude) {
73
							map.setCenter(pos);
74
						}
75
					}
76
				});
77
			}
78
		});
79
80
		initHeatmap();
81
	    initSelector(pokeimg_suffix, show_encounter_stats);
82
	});
83
}
84
85
function initSelector(pokeimg_suffix, show_encounter_stats) {
86
	$('#heatmapSelector').click(function() {
87
		hideLive();
88
		showHeatmap();
89
		$('#heatmapSelector').addClass('active');
90
		$('#liveSelector').removeClass('active');
91
	});
92
	$('#liveSelector').click(function() {
93
		hideHeatmap();
94
		map.setMapTypeId(google.maps.MapTypeId.ROADMAP);
95
	    initLive(pokeimg_suffix, show_encounter_stats);
96
		$('#liveSelector').addClass('active');
97
		$('#heatmapSelector').removeClass('active');
98
	});
99
}
100
101
function initLive(pokeimg_suffix, show_encounter_stats) {
102
	showLive();
103
104
	if (show_encounter_stats) {
105
		$('#liveFilterSelector').rangeSlider({
106
			bounds: {
107
				min: 0,
108
				max: 100
109
			},
110
			defaultValues: {
111
				min: ivMin,
112
				max: ivMax
113
			},
114
			formatter: function(val) {
115
				return 'IV: ' + Math.round(val) + '%';
116
			}
117
		});
118
	}
119
120
	$('#liveFilterSelector').bind('valuesChanged', function(e, data) {
121
		clearTimeout(updateLiveTimeout);
122
		removePokemonMarkerByIv(data.values.min, data.values.max);
123
		ivMin = data.values.min;
124
		ivMax = data.values.max;
125
		updateLive(pokeimg_suffix, show_encounter_stats);
126
	});
127
	updateLive(pokeimg_suffix, show_encounter_stats);
128
}
129
130
function initHeatmap() {
131
	$.ajax({
132
		'type': 'GET',
133
		'global': false,
134
		'dataType': 'json',
135
		'url': 'core/process/aru.php',
136
		'data': {
137
			'type': 'pokemon_slider_init'
138
		}
139
	}).done(function(bounds) {
140
		initHeatmapData(bounds);
141
	});
142
}
143
144
function initHeatmapData(bounds) {
145
	var boundMin = new Date(bounds.min.replace(/-/g, '/'));
146
	var boundMax = new Date(bounds.max.replace(/-/g, '/'));
147
	var selectorMax = boundMax;
148
	var selectorMin = boundMin;
149
150
	// two weeks in millisec
151
	var twoWeeks = 12096e5;
152
	var maxMinus2Weeks = new Date(selectorMax.getTime() - twoWeeks);
153
	if (selectorMin < maxMinus2Weeks) {
154
		selectorMin = maxMinus2Weeks;
155
	}
156
157
	// dict with millisec => migration nr.
158
	var migrations = {};
159
	// start at 4 because 06. Oct 2016 was the 4th migration
160
	var migr_nr = 4;
161
	$('#timeSelector').dateRangeSlider({
162
		bounds: {
163
			min: boundMin,
164
			max: boundMax
165
		},
166
		defaultValues: {
167
			min: selectorMin,
168
			max: selectorMax
169
		},
170
		scales: [{
171
			first: function(value) {
172
				// 06. Oct 2016 (4th migration). 2 week schedule starts with this migration
173
				var migrationStart = new Date('2016-10-06T00:00:00Z');
174
				var now = new Date();
175
				var result = new Date();
176
				for (var migration = migrationStart; migration <= now; migration.setTime(migration.getTime() + twoWeeks)) {
177
					if (migration >= value) {
178
						result = migration;
179
						migrations[result.getTime()] = migr_nr;
180
						break;
181
					}
182
					migr_nr++;
183
				}
184
				return result;
185
			},
186
			next: function(value) {
187
				var next = new Date(new Date(value).setTime(value.getTime() + twoWeeks));
188
				migr_nr++;
189
				migrations[next.getTime()] = migr_nr;
190
				return next;
191
			},
192
			label: function(value) {
193
				if (isMobileDevice() && isTouchDevice()) {
194
					return '#' + migrations[value.getTime()];
195
				}
196
				return 'Migration #' + migrations[value.getTime()];
197
			},
198
		}]
199
	});
200
	createHeatmap();
201
}
202
203
function createHeatmap() {
204
205
	heatmap = new google.maps.visualization.HeatmapLayer({
206
		data: [],
207
		map: map
208
	});
209
210
	var gradient = [
211
		'rgba(0, 255, 255, 0)',
212
		'rgba(0, 255, 255, 1)',
213
		'rgba(0, 191, 255, 1)',
214
		'rgba(0, 127, 255, 1)',
215
		'rgba(0, 63, 255, 1)',
216
		'rgba(0, 0, 255, 1)',
217
		'rgba(0, 0, 223, 1)',
218
		'rgba(0, 0, 191, 1)',
219
		'rgba(0, 0, 159, 1)',
220
		'rgba(0, 0, 127, 1)',
221
		'rgba(63, 0, 91, 1)',
222
		'rgba(127, 0, 63, 1)',
223
		'rgba(191, 0, 31, 1)',
224
		'rgba(255, 0, 0, 1)'
225
	];
226
	heatmap.set('gradient', gradient);
227
	heatmap.setMap(map);
228
	$('#timeSelector').bind('valuesChanged', function() { updateHeatmap() });
229
	$('#timeSelector').dateRangeSlider('min'); // will trigger valuesChanged
230
}
231
232
function updateHeatmap() {
233
	var dateMin = $('#timeSelector').dateRangeSlider('min');
234
	var dateMax = $('#timeSelector').dateRangeSlider('max');
235
	$('#loaderContainer').show();
236
	$.ajax({
237
		'type': 'GET',
238
		'global': false,
239
		'dataType': 'json',
240
		'url': 'core/process/aru.php',
241
		'data': {
242
			'type': 'pokemon_heatmap_points',
243
			'pokemon_id': pokemon_id,
244
			'start': Math.floor(dateMin.getTime() / 1000),
245
			'end': Math.floor(dateMax.getTime() / 1000)
246
		}
247
	}).done(function(points) {
248
		var googlePoints = [];
249
		for (var i = 0; i < points.length; i++) {
250
			googlePoints.push(new google.maps.LatLng(points[i].latitude, points[i].longitude))
251
		}
252
		var newPoints = new google.maps.MVCArray(googlePoints);
253
		heatmap.set('data', newPoints);
254
		$('#loaderContainer').hide();
255
	});
256
}
257
258
function hideHeatmap() {
259
	$('#timeFilterContainer').hide();
260
	heatmap.set('map', null);
261
}
262
263
function showHeatmap() {
264
	$('#timeFilterContainer').show();
265
	heatmap.set('map', map);
266
	hideLive();
267
}
268
269
function hideLive() {
270
	$('#liveFilterContainer').hide();
271
	clearTimeout(updateLiveTimeout);
272
	clearPokemonMarkers();
273
}
274
275
function showLive() {
276
	hideHeatmap();
277
	clearTimeout(updateLiveTimeout);
278
	$('#liveFilterContainer').show();
279
280
}
281
282
function updateLive(pokeimg_suffix, show_encounter_stats) {
283
	$.ajax({
284
		'type': 'POST',
285
		'global': false,
286
		'dataType': 'json',
287
		'url': 'core/process/aru.php',
288
		'data': {
289
			'type': 'pokemon_live',
290
			'pokemon_id': pokemon_id,
291
			'inmap_pokemons': extractEncountersId(),
292
			'ivMin': ivMin,
293
			'ivMax': ivMax
294
		}
295
	}).done(function(pokemons) {
296
		for (var i = 0; i < pokemons.points.length; i++) {
297
		    addPokemonMarker(pokemons.points[i], pokeimg_suffix, pokemons.locale, show_encounter_stats)
298
		}
299
		updateLiveTimeout = setTimeout(function() { updateLive(pokeimg_suffix) }, 5000);
300
	});
301
}
302
303
function addPokemonMarker(pokemon, pokeimg_suffix, locale, show_encounter_stats) {
304
	var image = {
305
		url: 'core/pokemons/' + pokemon.pokemon_id + pokeimg_suffix,
306
		scaledSize: new google.maps.Size(32, 32),
307
		origin: new google.maps.Point(0, 0),
308
		anchor: new google.maps.Point(16, 16),
309
		labelOrigin: new google.maps.Point(16, 36)
310
	};
311
	var encounter = false;
312
	var ivPercent = 100;
313
314
	if (show_encounter_stats) {
315
		if (pokemon.individual_attack !== null) {
316
			encounter = true;
317
			ivPercent = ((100 / 45) * (parseInt(pokemon.individual_attack) + parseInt(pokemon.individual_defense) + parseInt(pokemon.individual_stamina))).toFixed(2);
318
		}
319
        }
320
321
	var marker = new google.maps.Marker({
322
		position: { lat: parseFloat(pokemon.latitude), lng: parseFloat(pokemon.longitude) },
323
		map: map,
324
		icon: image,
325
		encounterId: pokemon.encounter_id,
326
		ivPercent: ivPercent
327
	});
328
	if (encounter) {
329
		marker.setLabel({
330
			color: getIvColor(ivPercent),
331
			text: ivPercent + '%'
332
		});
333
	}
334
	var contentString = '<div>' +
335
		'<h4> ' + pokemon.name + ' #' + pokemon.pokemon_id + (encounter ? ' IV: ' + ivPercent + '% ' : '') + '</h4>' +
336
		'<div id="bodyContent">' +
337
		'<p class="disappear_time_display text-center">' + pokemon.disappear_time_real + '<span class="disappear_time_display_timeleft"></span></p>';
338
	if (encounter) {
339
		contentString +=
340
			'<p></p>' +
341
			'<div class="progress" style="height: 6px; width: 120px; margin-bottom: 10px; margin-top: 2px; margin-left: auto; margin-right: auto">' +
342
			'<div title="' + locale.ivAttack + ': ' + pokemon.individual_attack + '" class="progress-bar progress-bar-danger" role="progressbar" aria-valuenow="' + pokemon.individual_attack + '" aria-valuemin="0" aria-valuemax="45" style="width: ' + (((100 / 15) * pokemon.individual_attack) / 3) + '%">' +
343
			'<span class="sr-only">' + locale.ivAttack + ': ' + pokemon.individual_attack + '</span>' +
344
			'</div>' +
345
			'<div title="' + locale.ivDefense + ': ' + pokemon.individual_defense + '" class="progress-bar progress-bar-info" role="progressbar" aria-valuenow="' + pokemon.individual_defense + '" aria-valuemin="0" aria-valuemax="45" style="width: ' + (((100 / 15) * pokemon.individual_defense) / 3) + '%">' +
346
			'<span class="sr-only">' + locale.ivDefense + ': ' + pokemon.individual_defense + '</span>' +
347
			'</div>' +
348
			'<div title="' + locale.ivStamina + ': ' + pokemon.individual_stamina + '" class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="' + pokemon.individual_stamina + '" aria-valuemin="0" aria-valuemax="45" style="width: ' + (((100 / 15) * pokemon.individual_stamina) / 3) + '%">' +
349
			'<span class="sr-only">' + locale.ivStamina + ': ' + pokemon.individual_stamina + '</span>' +
350
			'</div>' +
351
			'</div>' +
352
			'<p class="text-center">(' + pokemon.individual_attack + "/" + pokemon.individual_defense + "/" + pokemon.individual_stamina + ')</p>' +
353
			'<p class="text-center">' + pokemon.quick_move + "/" + pokemon.charge_move + '</p>';
354
	}
355
	contentString += '</div>';
356
357
	var infoWindow = new google.maps.InfoWindow({
358
		content: contentString
359
	});
360
	infoWindow.isClickOpen = false;
361
	marker.addListener('click', function() {
362
		infoWindow.isClickOpen = true;
363
		infoWindow.open(map, this);
364
	});
365
	google.maps.event.addListener(infoWindow, 'closeclick', function() {
366
		this.isClickOpen = false;
367
	});
368
	marker.addListener('mouseover', function() {
369
		infoWindow.open(map, this);
370
	});
371
372
	// assuming you also want to hide the infowindow when user mouses-out
373
	marker.addListener('mouseout', function() {
374
		if (infoWindow.isClickOpen === false) {
375
			infoWindow.close();
376
		}
377
	});
378
	pokemonMarkers.push(marker);
379
	var now = new Date().getTime();
380
	var endTime = new Date(pokemon.disappear_time_real.replace(/-/g, '/')).getTime();
381
382
	setTimeout(function() { removePokemonMarker(pokemon.encounter_id) }, endTime - now);
383
}
384
385
386
function getIvColor(ivPercent) {
387
	var ivColor = 'rgba(0, 0, 0, 0)';
388
	if (ivPercent > 80) {
389
		ivColor = 'rgba(0, 0, 255, 0.70)';
390
	}
391
	if (ivPercent > 90) {
392
		ivColor = 'rgba(246, 178, 107, 0.90)';
393
	}
394
	if (ivPercent > 99) {
395
		ivColor = 'rgba(255, 0, 0, 1)';
396
	}
397
	return ivColor;
398
}
399
400
function clearPokemonMarkers() {
401
	for (var i = 0; i < pokemonMarkers.length; i++) {
402
		pokemonMarkers[i].setMap(null);
403
	}
404
	pokemonMarkers = [];
405
}
406
407
function removePokemonMarker(encounter_id) {
408
	for (var i = 0; i < pokemonMarkers.length; i++) {
409
		if (pokemonMarkers[i].encounterId == encounter_id) {
410
			pokemonMarkers[i].setMap(null);
411
			pokemonMarkers.splice(i, 1);
412
			break;
413
		}
414
	}
415
}
416
417
function removePokemonMarkerByIv(ivMin, ivMax) {
418
	var cleanMarkers = [];
419
	for (var i = 0; i < pokemonMarkers.length; i++) {
420
		if (pokemonMarkers[i].ivPercent < ivMin || pokemonMarkers[i].ivPercent > ivMax) {
421
			pokemonMarkers[i].setMap(null);
422
		} else {
423
			cleanMarkers.push(pokemonMarkers[i]);
424
		}
425
	}
426
	pokemonMarkers = cleanMarkers;
427
}
428
429
function extractEncountersId() {
430
	var inmapEncounter = [];
431
	for (var i = 0; i < pokemonMarkers.length; i++) {
432
		inmapEncounter[i] = pokemonMarkers[i].encounterId;
433
	}
434
	return inmapEncounter;
435
}
436
437
function isTouchDevice() {
438
	// Should cover most browsers
439
	return 'ontouchstart' in window || navigator.maxTouchPoints
440
}
441
442
function isMobileDevice() {
443
	// Basic mobile OS (not browser) detection
444
	return (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent))
445
}
446