PageSourceSearch

https://caspar-olevian-portal.de/application/themes/cop/js/cop-europa-map.js

js caspar-olevian-portal.de collected 2026-10-08 08:34:40 UTC 58,519 bytes, 1,647 lines download raw bytes

1/**
2 * Europa map module for Caspar Olevian Portal
3 * 
4 * @author Sheng, Li [email protected]
5 * @version 1.0.0
6 * 
7 */
8
9/**
10 * 
11 * @class CopEuMap
12 */
13function CopEuMap () {}
14
15// config
16CopEuMap.prototype.containerId = '';
17CopEuMap.prototype.mapWidth = 0;
18CopEuMap.prototype.mapHeight = 0;
19CopEuMap.prototype.mapImageW = 0;
20CopEuMap.prototype.mapImageH = 0;
21CopEuMap.prototype.mapBoundariesW = 24;
22CopEuMap.prototype.gapToMapBorder = 0;
23CopEuMap.prototype.containerWidth = 0;
24CopEuMap.prototype.containerHeight = 0;
25CopEuMap.prototype.leftTopLon = 0;
26CopEuMap.prototype.leftTopLat = 0;
27CopEuMap.prototype.rightBottomLon = 0;
28CopEuMap.prototype.rightBottomLat = 0;
29
30CopEuMap.prototype.animationDuration = 400;
31
32CopEuMap.prototype.staysList = [];
33
34// helping parameters
35CopEuMap.prototype.pixelLon = 0;
36CopEuMap.prototype.pixelLat = 0;
37
38CopEuMap.prototype.currentStayNum = -1;
39
40// attributes for map
41CopEuMap.prototype.mapDrawarea = null;
42CopEuMap.prototype.timeControlDrawarea = null;
43CopEuMap.prototype.backgroundPicName = '';
44CopEuMap.prototype.copJson = null;
45CopEuMap.prototype.copBigCities = null;
46CopEuMap.prototype.lutherCities = null;
47CopEuMap.prototype.isShowCityname = true;
48CopEuMap.prototype.isShowBigCi = false;
49CopEuMap.prototype.isShowLutherCi = false;
50CopEuMap.prototype.isShowArrowLayer = false;
51CopEuMap.prototype.isShowbgLayer = false;
52CopEuMap.prototype.isShowscaleLayer = true;
53
54
55CopEuMap.prototype.pointRadiusBase = 10;
56CopEuMap.prototype.pointRadiusFactor = 4;
57
58
59CopEuMap.prototype.init = function (containerId, backgroundPicName, backgroundColorPicName,
60		w, h, leftTopLon, leftTopLat, rightBottomLon, rightBottomLat,
61		copJson, copBigCities, lutherCities) {
62	this.containerId = containerId;
63	this.backgroundPicName = backgroundPicName;
64	this.backgroundColorPicName = backgroundColorPicName;
65	this.mapImageH = h;
66	this.mapImageW = w;
67	h = $('#'+containerId).height();
68	this.containerHeight = h;
69	this.containerWidth = $('#'+containerId).width();
70	this.mapHeight = h - 2 * this.mapBoundariesW;
71	// this.mapWidth = h;
72	// w = h;
73	this.mapWidth = this.containerWidth / 10 * 6.6 - 2 * this.mapBoundariesW;
74	// this.timeControlWidth = this.containerWidth / 10 * 1;
75	this.timeControlWidth = 80;
76	this.mapContainerWidth = this.mapWidth + this.timeControlWidth + 2 * this.mapBoundariesW;
77	this.pointRadiusBase = this.containerHeight / 75;
78	w = this.mapWidth;
79	// $('#'+containerId).height(this.containerHeight);
80	this.leftTopLat = leftTopLat;
81	this.leftTopLon = leftTopLon;
82	this.rightBottomLat = rightBottomLat;
83	this.rightBottomLon = rightBottomLon;
84
85	this.copJson = copJson;
86	this.copBigCities = copBigCities;
87	this.lutherCities = lutherCities;
88
89	// this.pointR = Math.log(duration) * this.pointRadiusFactor + this.pointRadiusBase;
90	this.coPointD = this.pointRadiusBase * 1.4;
91	this.lutherD = this.pointRadiusBase;
92	this.bigCityD = this.pointRadiusBase;
93
94	this.pixelLon = 1.0 * this.mapWidth / (rightBottomLon - leftTopLon);
95	this.pixelLat = 1.0 * this.mapHeight / (leftTopLat - rightBottomLat);
96
97	// console.log('pixLon-lat: ' + this.pixelLon + '  ' + this.pixelLat);
98
99	this.showingElements = [null];
100
101	// init draw area
102	this.createMap();
103
104	// react for parameters
105	var cityNumParam = this.getUrlParameter('city');
106	var stayNumParam = this.getUrlParameter('stay');
107	if (stayNumParam) {
108		var stayNum = parseInt(stayNumParam);
109		this.clickElements({stayC: this.staysList[stayNum-1]});
110	} else if (cityNumParam) {
111		var cityNum = parseInt(cityNumParam);
112		var f = null;
113		for (var i = 0; i < this.copJson.features.length; i++) {
114			var locId = this.copJson.features[i].properties.locId;
115			if (cityNum === locId) {
116				f = this.copJson.features[i];
117				break;
118			}
119		}
120		this.clickElements({feature: f});
121	} else {
122		this.clickElements({stayC: this.staysList[0]});
123
124	}
125};
126
127CopEuMap.prototype.calculatePixPosByLonLat = function (point) {
128	var lon = point[0];
129	var lat = point[1];
130	var x = 1.0 * this.pixelLon * (lon - this.leftTopLon) + this.mapBoundariesW; 
131	var y = this.mapHeight - 1.0 * this.pixelLat * (lat - this.rightBottomLat) + this.mapBoundariesW;
132	// console.log(point[0] + '  ' + point[1] + ': ' );
133	// console.log(x + ' ' + y);
134	return {x: x, y: y};
135};
136
137CopEuMap.prototype.createMap = function () {
138	var _this = this;
139	if (this.mapDrawarea !== null) this.mapDrawarea.remove();
140	this.mapDrawarea = SVG(this.containerId).size(this.mapContainerWidth, this.containerHeight).attr('id', this.containerId+'_mapDrawarea');
141	var d = this.mapDrawarea;
142
143	this.patternMap = d.pattern('100%', '100%', function (add) {
144		_this.mapBg = add.image(this.backgroundPicName, 1, 1);
145		_this.mapBg.attr('id', 'cop-europamap-mapImage-image');
146	}).attr({x: this.mapBoundariesW, y: this.mapBoundariesW}).attr('id', 'cop-europamap-mapImage').attr('patternContentUnits', 'objectBoundingBox')
147	.attr('preserveAspectRatio', 'none');
148
149	this.patternBG = d.pattern('100%', '100%', function (add) {
150		_this.controlBg = add.image('cop-img/europamap/papier-bg-resized.jpg', 1, 1);
151		_this.controlBg.attr('id', 'cop-europamap-bgImage-image');
152	}).attr({x: this.mapBoundariesW, y: this.mapBoundariesW}).attr('id', 'cop-europamap-bgImage').attr('patternContentUnits', 'objectBoundingBox')
153	.attr('preserveAspectRatio', 'none');
154
155	this.mapRect = d.rect(this.mapWidth, this.mapHeight)
156	.attr({x: this.mapBoundariesW, y: this.mapBoundariesW}).attr('fill', 'url(#cop-europamap-mapImage)').attr('id', 'cop_eu_map_map');
157	this.mapBg.load(this.backgroundPicName);
158	// _this.mapBg = d.image(this.backgroundPicName).attr('preserveAspectRatio', 'none');
159
160
161	// map boundaries
162	this.drawMapBoundaries();
163
164	// draw big cities 
165	for (var i = 0; i < this.copBigCities.features.length; i++) {
166		this.copBigCities.features[i].onMapElements = {};
167		this.drawBigCities(this.copBigCities.features[i]);
168	}
169
170	// draw luther cities
171	for (var i = 0; i < this.lutherCities.features.length; i++) {
172		this.lutherCities.features[i].onMapElements = {};
173		this.drawLutherCities(this.lutherCities.features[i]);
174	}
175
176	// draw cop json elements
177	var features = this.copJson['features'];
178
179	for(var i = 0; i < features.length; i++) {
180		features[i].onMapElements = {};
181	}
182
183	for(var i = 0; i < features.length; i++) {
184		this.arrowHeadW = this.pointRadiusBase / 3;
185		var arrowHeadW = this.arrowHeadW;
186		this.arrowMarker = d.marker(arrowHeadW, arrowHeadW, function(add){
187			var p = add.path('M0,0 L0,'+arrowHeadW+' L'+arrowHeadW+' '+arrowHeadW/2+' z').addClass('cop_eu_map_location_arrow_head');
188		}).attr('refX', arrowHeadW/2).attr('refY', arrowHeadW/2);
189		this.drawArrowsOnMap(features[i]);
190	}
191	for(var i = 0; i < features.length; i++) {
192		this.drawPointOnMap(features[i]);
193		features[i].clicked = false;
194	}
195	for(var i = 0; i < features.length; i++) {
196		this.drawTextOnMap(features[i]);
197	}
198
199	// layer control elements
200	this.drawLayerControlElements();
201
202	// time cotrol elements
203	this.drawTimeControlElements();
204	for(var i = 0; i < features.length; i++) {
205		this.addFromArrowsToDataStructure(features[i]);
206	}
207
208	// info column
209	this.addInfoElements();
210
211
212};
213
214CopEuMap.prototype.drawMapBoundaries = function () {
215	var _this = this;
216	var d = this.mapDrawarea;
217	var mapBoundariesW = this.mapBoundariesW;
218
219	// scales
220	
221	var scaleLonSum = this.rightBottomLon - this.leftTopLon;
222	var scaleLatSum = this.leftTopLat - this.rightBottomLat;
223	var scaleLonWidth = this.mapWidth / scaleLonSum;
224	var scaleLatHeight = this.mapHeight / scaleLatSum;
225	
226	var scaleStrokeWidth = 0.3;
227
228	this.scales = {lons: [], lats: []};
229
230	var textMapGap = 1;
231	//lon
232	for (var i = 0; i < scaleLonSum; i++) {
233		var currentLon = i + Math.ceil(this.leftTopLon);
234		if (currentLon % 5 === 0) {
235			var p1X = this.calculatePixPosByLonLat([currentLon, 0]).x;
236			var p2X = p1X;
237			var p1Y = mapBoundariesW, p2Y = this.mapHeight+mapBoundariesW;
238			var line = d.line(p1X, p1Y, p2X, p2Y).stroke({width: scaleStrokeWidth});
239			this.scales.lons.push(line);
240			var tbottom = d.text(''+currentLon+'°').addClass('cop_eu_map_location_scale_text');
241			tbottom.x(p2X - tbottom.bbox().width / 2).y(p2Y + textMapGap + 1);
242			this.scales.lons.push(tbottom);
243			var ttop = d.text(''+currentLon+'°').addClass('cop_eu_map_location_scale_text');
244			ttop.x(p1X - ttop.bbox().width / 2).y(p1Y-ttop.bbox().height - textMapGap);
245			this.scales.lons.push(ttop);
246		}
247	}
248	//lat
249	for (var i = Math.ceil(scaleLatSum); i >= 0; i--) {
250		var currentLat = Math.ceil(this.leftTopLat) - i;
251		if (currentLat % 5 === 0) {
252			var p1X = mapBoundariesW, p2X = this.mapWidth+mapBoundariesW;
253			var p1Y = this.calculatePixPosByLonLat([0, currentLat]).y , p2Y = p1Y;
254			var line = d.line(p1X, p1Y, p2X, p2Y).stroke({width: scaleStrokeWidth});
255			this.scales.lats.push(line);
256			var tleft = d.text(''+currentLat+'°').addClass('cop_eu_map_location_scale_text');
257			tleft.x(p1X - tleft.bbox().width - textMapGap).y(p2Y - tleft.bbox().height / 2);
258			this.scales.lats.push(tleft);
259			var tright = d.text(''+currentLat+'°').addClass('cop_eu_map_location_scale_text');
260			tright.x(p2X + textMapGap + 2).y(p2Y - tright.bbox().height / 2);
261			this.scales.lats.push(tright);
262
263		}
264	}
265
266};
267
268CopEuMap.prototype.drawPointOnMap = function (f) {
269	var _this = this;
270	var d = this.mapDrawarea;
271	var geometry = f['geometry'];
272	var coordinates = geometry['coordinates'];
273	
274	var location = f.properties['location'];
275	var stayInfo = f.properties.stayInfo;
276	var locId = f.properties.locId;
277
278	var point = null;
279	var position = null;
280	if (geometry['type'] === 'Point') {
281		point = coordinates; 
282		position = this.calculatePixPosByLonLat(point);
283
284		// create point
285		var pointOnMap = d.circle(this.coPointD).x(position.x).y(position.y).addClass('cop_eu_map_location cop_eu_map_location_point');
286		f.onMapElements.pointOnMap = pointOnMap;
287		
288		// add events to point
289		pointOnMap.mouseover(function (evt) {
290			_this.hightlightMapElements({feature: f});
291			
292		});
293		pointOnMap.mouseout(function (evt) {
294			_this.unhightlightMapElements({feature: f});
295		});
296		pointOnMap.click(function (evt) {
297
298			_this.clickElements({feature: f});
299		});
300
301	} else if (geometry['type'] === 'MultiPoint') {
302		for (var i = 0; i < coordinates.length; i++) {
303			point = coordinates[i];
304		}
305	}
306	
307};
308
309CopEuMap.prototype.drawTextOnMap = function (f) {
310	d = this.mapDrawarea;
311	var geometry = f['geometry'];
312	var coordinates = geometry['coordinates'];
313	
314	var date = f.properties['date'];
315	var location = f.properties['location'];
316	var stayInfo = f.properties.stayInfo;
317	var locId = f.properties.locId;
318
319	var point = null;
320	var position = null;
321	if (geometry['type'] === 'Point') {
322		point = coordinates; 
323		position = this.calculatePixPosByLonLat(point);
324		var r = this.coPointD / 2;
325
326		// create location label
327		var locationLabelHalo = d.text(location).addClass('cop_eu_map_location_text_halo');
328		var labelH = locationLabelHalo.bbox().height;
329		var labelW = locationLabelHalo.bbox().width;
330		if (location === 'Heidelberg') { // text is at right side
331			var labelX = position.x + r * 3;
332			var labelY = position.y - labelH / 5;
333		} else if (location === 'Genf') { // text is at bottom left of point
334			var labelX = position.x - labelW - 3*r/2;
335			var labelY = position.y + r * 0;
336		} else if (location === 'Bourges') { // text is at bottom of point
337			var labelX = position.x - labelW / 2 + 2*r/2;
338			var labelY = position.y + r * 3;
339		} else if (location === 'Orléans' || location === 'Herborn' || location === 'Paris') { // text is at left side
340			var labelX = position.x - 4*r/2 - labelW;
341			var labelY = position.y + 0* labelH / 2;
342		} else {
343			var labelX = position.x - labelW / 2 + 2*r/2;
344			var labelY = position.y - labelH - 1*r/2;
345		}
346		locationLabelHalo.x(labelX).y(labelY);
347		var locationLabel = d.text(location).addClass('cop_eu_map_location cop_eu_map_location_text').x(labelX).y(labelY);
348
349		f.onMapElements.locationLabelHalo = locationLabelHalo;
350		f.onMapElements.locationLabel = locationLabel;
351		// f.onMapElements.locationLabelHalo.hide();
352		// f.onMapElements.locationLabel.hide();
353
354
355	} else if (geometry['type'] === 'MultiPoint') {
356		// for (var i = 0; i < coordinates.length; i++) {
357		// 	point = coordinates[i];
358		// }
359	}
360};
361
362CopEuMap.prototype.drawBigCities = function (f) {
363	d = this.mapDrawarea;
364	var geometry = f['geometry'];
365	var coordinates = geometry['coordinates'];
366	var location = f.properties['location'];
367
368	var point = null;
369	var position = null;
370	if (geometry['type'] === 'Point') {
371		point = coordinates; 
372		position = this.calculatePixPosByLonLat(point);
373		// position.x += this.mapBoundariesW;
374		// position.y += this.mapBoundariesW;
375
376		// create point
377		var r = this.bigCityD;
378		var pointOnMap = d.circle(r).x(position.x).y(position.y).addClass('cop_eu_map_location cop_eu_map_location_big_city_point');
379		f.onMapElements.pointOnMapBigCi = pointOnMap;
380
381		// create location label
382		var locationLabelHalo = d.text(location).addClass('cop_eu_map_location_big_city_text_halo');
383		var labelH = locationLabelHalo.bbox().height;
384		var labelW = locationLabelHalo.bbox().width;
385		if (location === 'Berlin') {
386			var labelX = position.x - pointOnMap.attr('r')/2  - labelW;
387			var labelY = position.y - labelH / 5;
388		} else if (location === 'Augsburg') {
389			var labelX = position.x - labelW / 2 + r/2;
390			var labelY = position.y - labelH - 0 ;
391		} else if (location === '') { // text is at bottom of point
392			var labelX = position.x - labelW / 2 + r/2;
393			var labelY = position.y + labelH -3 ;
394		}  else {
395			var labelX = position.x + pointOnMap.attr('r') * 2.5;
396			var labelY = position.y - labelH / 5;
397		}
398		locationLabelHalo.x(labelX).y(labelY);
399		var locationLabel = d.text(location).addClass('cop_eu_map_location cop_eu_map_location_big_city_text').x(labelX).y(labelY);
400		f.onMapElements.locationLabelHalo = locationLabelHalo;
401		f.onMapElements.locationLabel = locationLabel;
402		locationLabel.hide();
403		locationLabelHalo.hide();
404		pointOnMap.hide();
405
406	} else if (geometry['type'] === 'MultiPoint') {
407		// for (var i = 0; i < coordinates.length; i++) {
408		// 	point = coordinates[i];
409		// }
410	}
411};
412
413CopEuMap.prototype.drawLutherCities = function (f) {
414	d = this.mapDrawarea;
415	var geometry = f['geometry'];
416	var coordinates = geometry['coordinates'];
417	var location = f.properties['location'];
418
419	var point = null;
420	var position = null;
421	if (geometry['type'] === 'Point') {
422		point = coordinates; 
423		position = this.calculatePixPosByLonLat(point);
424
425		// create point
426		var r = this.lutherD;
427		var pointOnMap = d.circle(r).x(position.x).y(position.y).addClass('cop_eu_map_location cop_eu_map_location_luther_city_point');
428		f.onMapElements.pointOnMapLutherCi = pointOnMap;
429
430		// create location label
431		var locationLabelHalo = d.text(location).addClass('cop_eu_map_location_luther_city_text_halo');
432		var labelH = locationLabelHalo.bbox().height;
433		var labelW = locationLabelHalo.bbox().width;
434		if (location === 'Marburg' || location === 'Erfurt' || location === 'Eisleben') {
435			var labelX = position.x + pointOnMap.attr('r') * 2.5;
436			var labelY = position.y - labelH / 5;
437		} else if (location === 'Worms') {
438			var labelX = position.x - labelW - 2*r/2;
439			var labelY = position.y - labelH / 5;
440		} else if (location === 'Torgau') {
441			var labelX = position.x - labelW / 2 + r/2;
442			var labelY = position.y + r;
443		} else {
444			var labelX = position.x - labelW / 2 + r/2;
445			var labelY = position.y - labelH - 0;
446		}
447		locationLabelHalo.x(labelX).y(labelY);
448		var locationLabel = d.text(location).addClass('cop_eu_map_location cop_eu_map_location_luther_city_text').x(labelX).y(labelY);
449		f.onMapElements.locationLabelHalo = locationLabelHalo;
450		f.onMapElements.locationLabel = locationLabel;
451		locationLabel.hide();
452		locationLabelHalo.hide();
453		pointOnMap.hide();
454
455	} else if (geometry['type'] === 'MultiPoint') {
456		// for (var i = 0; i < coordinates.length; i++) {
457		// 	point = coordinates[i];
458		// }
459	}
460};
461
462CopEuMap.prototype.drawArrowsOnMap = function (f) {
463	var d = this.mapDrawarea;
464	var geometry = f['geometry'];
465	var coordinates = geometry['coordinates'];
466	
467	var location = f.properties['location'];
468	var stayInfo = f.properties.stayInfo;
469	var locId = f.properties.locId;
470	f.onMapElements.stays = [];
471
472	var point = null;
473	var position = null;
474	if (geometry['type'] === 'Point') {
475		point = coordinates; 
476		position = this.calculatePixPosByLonLat(point);
477		var pR = this.coPointD / 2;
478		var pX = position.x + pR;
479		var pY = position.y + pR;
480
481		// draw arrows
482		
483		for (var i = 0; i < stayInfo.length; i ++) {
484			var tempToId = stayInfo[i].to;
485			// if (tempToId === 0)
486			// 	continue;
487			var date = stayInfo[i].date;
488			var description = stayInfo[i].description;
489			var duration = stayInfo[i].duration;
490			var toLocation = '';
491			var tpX = pX, tpY = pY, tpR = pR;
492			// find next position
493			for (var j = 0; j < this.copJson.features.length; j ++ ) {
494				var tempF = this.copJson.features[j];
495				if (tempF.properties.locId === tempToId) {
496					var tempToPoint = tempF.geometry.coordinates;
497					var tempToPosition = this.calculatePixPosByLonLat(tempToPoint);
498					toLocation = tempF.properties.location;
499					tpR = this.coPointD / 2;
500					tpX = tempToPosition.x + tpR;
501					tpY = tempToPosition.y + tpR;
502					tpR += this.arrowHeadW / 2;
503					break;
504				}
505			}
506			var curveStartX = 0, curveStartY = 0, 
507				arrowPointX = 0, arrowPointY = 0,
508				curveStraightLength = 0,
509				curveRatio = 0,
510				curveStraightMidX = 0,
511				curveStraightMidY = 0,
512				supportPointX = 0, supportPointY = 0;
513			// calculate arrow curve points
514			if (tpX > pX && tpY < pY) { // top right of this point
515				// tpY = tpY + tpR;
516				curveStartX = pX, curveStartY = pY;
517				arrowPointX = tpX - tpR;
518				arrowPointY = tpY + tpR;
519				curveStraightLength = Math.sqrt(Math.pow(Math.abs(arrowPointX - pX), 2) + Math.pow(Math.abs(arrowPointY - pY), 2));
520				curveRatio = curveStraightLength / 4;
521
522				if (Math.abs(tpX - pX) < curveRatio * 2) {
523					arrowPointX = tpX + tpR;
524				}
525				
526				curveStraightMidX = pX + (arrowPointX - pX) / 2;
527				curveStraightMidY = pY + (arrowPointY - pY) / 2;
528
529				supportPointX = curveStraightMidX + Math.sqrt(Math.pow(curveRatio,2)/(1+Math.pow(Math.pow(Math.abs(arrowPointX - pX), 2) / Math.pow(Math.abs(arrowPointY - pY), 2),2)));
530				supportPointY = curveStraightMidY + Math.sqrt(Math.pow(curveRatio,2)/(1+Math.pow(Math.pow(Math.abs(arrowPointY - pY), 2) / Math.pow(Math.abs(arrowPointX - pX), 2),2)));
531
532
533			} else if (tpX > pX && tpY > pY) { // bottom right of this point
534				// tpY = tpY - tpR;
535				curveStartX = pX, curveStartY = pY;
536				arrowPointX = tpX - tpR;
537				arrowPointY = tpY - tpR;
538				curveStraightLength = Math.sqrt(Math.pow(Math.abs(arrowPointX - pX), 2) + Math.pow(Math.abs(arrowPointY - pY), 2));
539				curveRatio = curveStraightLength / 4;
540
541				
542				if (Math.abs(tpY - pY) < curveRatio * 2) {
543					arrowPointY = tpY + tpR;
544				}
545
546				curveStraightMidX = pX + (arrowPointX - pX) / 2;
547				curveStraightMidY = pY + (arrowPointY - pY) / 2;
548
549				supportPointX = curveStraightMidX - Math.sqrt(Math.pow(curveRatio,2)/(1+Math.pow(Math.pow(Math.abs(arrowPointX - pX), 2) / Math.pow(Math.abs(arrowPointY - pY), 2),2)));
550				supportPointY = curveStraightMidY + Math.sqrt(Math.pow(curveRatio,2)/(1+Math.pow(Math.pow(Math.abs(arrowPointY - pY), 2) / Math.pow(Math.abs(arrowPointX - pX), 2),2)));
551
552			} else if (tpX < pX && tpY < pY) { // top left of this point
553				// tpY = tpY + tpR;
554				curveStartX = pX, curveStartY = pY;
555				arrowPointX = tpX + tpR;
556				arrowPointY = tpY + tpR;
557
558				curveStraightLength = Math.sqrt(Math.pow(Math.abs(arrowPointX - pX), 2) + Math.pow(Math.abs(arrowPointY - pY), 2));
559				curveRatio = curveStraightLength / 4;
560				
561				if (Math.abs(arrowPointY - pY) < curveRatio * 2) {
562					arrowPointY = tpY - tpR;
563				}
564
565				curveStraightMidX = pX + (arrowPointX - pX) / 2;
566				curveStraightMidY = pY + (arrowPointY - pY) / 2;
567
568				supportPointX = curveStraightMidX + Math.sqrt(Math.pow(curveRatio,2)/(1+Math.pow(Math.pow(Math.abs(arrowPointX - pX), 2) / Math.pow(Math.abs(arrowPointY - pY), 2),2)));
569				supportPointY = curveStraightMidY - Math.sqrt(Math.pow(curveRatio,2)/(1+Math.pow(Math.pow(Math.abs(arrowPointY - pY), 2) / Math.pow(Math.abs(arrowPointX - pX), 2),2)));
570
571			} else if (tpX < pX && tpY > pY) { // bottom left of this point
572				// tpY = tpY - tpR;
573				curveStartX = pX, curveStartY = pY;
574				arrowPointX = tpX + tpR;
575				arrowPointY = tpY - tpR;
576
577				curveStraightLength = Math.sqrt(Math.pow(Math.abs(arrowPointX - pX), 2) + Math.pow(Math.abs(arrowPointY - pY), 2));
578				curveRatio = curveStraightLength / 4;
579
580				
581				if (Math.abs(arrowPointX - pX) < curveRatio * 2) {
582					arrowPointX = tpX - tpR;
583				} else {
584					arrowPointX = tpX + tpR;
585				}
586				
587				curveStraightMidX = pX + (arrowPointX - pX) / 2;
588				curveStraightMidY = pY + (arrowPointY - pY) / 2;
589
590				supportPointX = curveStraightMidX - Math.sqrt(Math.pow(curveRatio,2)/(1+Math.pow(Math.pow(Math.abs(arrowPointX - pX), 2) / Math.pow(Math.abs(arrowPointY - pY), 2),2)));
591				supportPointY = curveStraightMidY - Math.sqrt(Math.pow(curveRatio,2)/(1+Math.pow(Math.pow(Math.abs(arrowPointY - pY), 2) / Math.pow(Math.abs(arrowPointX - pX), 2),2)));
592
593			} 
594
595			// manuel corect
596			if (location === 'Paris' && toLocation === 'Orléans') {
597				arrowPointY = tpY - tpR * 1.5;
598				arrowPointX = tpX;
599			}
600			if (location === 'Orléans' && toLocation === 'Bourges') {
601				arrowPointY = tpY + tpR * 0.5;
602				arrowPointX = tpX - tpR * 1.5;
603				supportPointX = curveStraightMidX - Math.sqrt(Math.pow(curveRatio*2,2)/(1+Math.pow(Math.pow(Math.abs(arrowPointX - pX), 2) / Math.pow(Math.abs(arrowPointY - pY), 2),2)));
604				supportPointY = curveStraightMidY + Math.sqrt(Math.pow(curveRatio*1,2)/(1+Math.pow(Math.pow(Math.abs(arrowPointY - pY), 2) / Math.pow(Math.abs(arrowPointX - pX), 2),2)));
605			}
606			if (location === 'Bad Berleburg' && toLocation === 'Herborn') {
607				arrowPointX = tpX + tpR / 2;
608				arrowPointY = tpY - tpR / 2 * 3;
609				var arrowPath = d.path('M '+curveStartX+' '+curveStartY+' L '+arrowPointX+' '+arrowPointY).addClass('cop_eu_map_location_arrow').attr('opacity', 0).marker('end', this.arrowMarker).attr('stroke-width', this.pointRadiusBase/3);
610			} else {
611
612				var arrowPath = d.path('M '+curveStartX+' '+curveStartY+' Q '+supportPointX+' '+supportPointY+' '+arrowPointX+' '+arrowPointY).addClass('cop_eu_map_location_arrow').attr('opacity', 0).marker('end', this.arrowMarker).attr('stroke-width', this.pointRadiusBase/3);
613			}
614			
615
616			f.onMapElements.stays.push({
617				fromId: locId, fromLocation: location, 
618				fromArrowInfo: null,
619				toId: tempToId, toLocation: toLocation, 
620				toArrowInfo: {showed: false, arrow: arrowPath}, 
621				description: description, 
622				date: moment(date).locale('de'), duration: duration, clicked: false});
623		}
624
625	} else if (geometry['type'] === 'MultiPoint') {
626		// for (var i = 0; i < coordinates.length; i++) {
627		// 	point = coordinates[i];
628		// }
629	}
630};
631
632CopEuMap.prototype.addFromArrowsToDataStructure = function (f) {
633	var locId = f.properties.locId;
634	f.onMapElements.fromArrows = [];
635	// find from points
636	var features = this.copJson.features;
637	for (var i = 0; i < features.length; i++) {
638		var tf = features[i];
639		var fromId = tf.properties.locId;
640		var stays = tf.onMapElements.stays;
641		for (var j = 0; j < stays.length; j++) {
642			var stay = stays[j];
643			for (var k = 1; k < this.staysList.length; k++) {
644				var stayC = this.staysList[k];
645				if (stayC.stay === stay) {
646					stay.fromArrowInfo = this.staysList[k-1].stay.toArrowInfo;
647				}
648			}
649		}
650	}
651
652};
653
654CopEuMap.prototype.showAllArrows = function () {
655	
656	for (var i = 0; i < this.staysList.length; i++) {
657		var stay = this.staysList[i].stay;
658		stay.toArrowInfo.arrow.animate(this.animationDuration).attr('opacity', 1);
659	}
660
661	
662};
663
664
665CopEuMap.prototype.hideAllArrows = function () {
666	for (var i = 0; i < this.staysList.length; i++) {
667		var stay = this.staysList[i].stay;
668		if (!stay.clicked) {
669			var shouldRemove = true;
670			for (var j = 0; j < this.staysList.length; j++) {
671				var tempStay = this.staysList[j].stay;
672				if (tempStay.clicked && tempStay.fromArrowInfo === stay.toArrowInfo) {
673					shouldRemove = false;
674				}
675			}
676			if (shouldRemove) {
677				stay.toArrowInfo.arrow.animate(this.animationDuration).attr('opacity', 0);
678			}
679		}
680	}
681};
682
683CopEuMap.prototype.showColorBg = function () {
684	this.mapBg.load(this.backgroundColorPicName); // for firefox
685	$('#cop-europamap-mapImage-image').attr('href', this.backgroundColorPicName); // for chrome
686};
687
688CopEuMap.prototype.hideColorBg = function () {
689	this.mapBg.load(this.backgroundPicName); // for firefox
690	$('#cop-europamap-mapImage-image').attr('href', this.backgroundPicName); // for chrome
691
692};
693
694CopEuMap.prototype.showScale = function () {
695	for (var i = 0; i < this.scales.lons.length; i++) {
696		this.scales.lons[i].show();
697	}
698	for (var i = 0; i < this.scales.lats.length; i++) {
699		this.scales.lats[i].show();
700	}
701};
702
703CopEuMap.prototype.hideScale = function () {
704	for (var i = 0; i < this.scales.lons.length; i++) {
705		this.scales.lons[i].hide();
706	}
707	for (var i = 0; i < this.scales.lats.length; i++) {
708		this.scales.lats[i].hide();
709	}
710
711
712};
713
714
715/**
716 * 
717 * @param {object} obj {feature: feature, stayC: stayC}
718 */
719CopEuMap.prototype.hightlightMapElements = function (obj) {
720
721	if (obj.feature) {
722		var f = obj.feature;
723		f.onMapElements.pointOnMap.removeClass('cop_eu_map_location_point');
724		f.onMapElements.pointOnMap.addClass('cop_eu_map_location_point_hover');
725		f.onMapElements.locationLabelHalo.show();
726		f.onMapElements.locationLabel.show();
727		for (var i = 0; i < f.onMapElements.stays.length; i++) {
728			var stay = f.onMapElements.stays[i];
729			var rect = stay.yearShowRect;
730			this.addShowingElements({type: 'timeRect', element: rect});
731			this.addShowingElements({type: 'timeText', element: stay.yearShowFromText});
732			this.addShowingElements({type: 'timeText', element: stay.yearShowToText});
733			var fromArrowInfo = stay.fromArrowInfo;
734			if (fromArrowInfo) {
735				this.addShowingElements({type: 'arrow', element: fromArrowInfo});
736			}
737			var toArrowInfo = stay.toArrowInfo;
738			this.addShowingElements({type: 'arrow', element: toArrowInfo});
739
740			if (f.clicked) {
741				stay.clicked = true;
742				stay.text.show();
743				for (var j = 0; j < this.staysList.length; j++) {
744					if (stay === this.staysList[j].stay) {
745						this.currentStayNum = j;
746						break;
747					}
748				}
749			}
750		}
751		// show info box, only when feature is clicked
752		if (f.clicked) {
753			this.addShowingElements({type: 'infoBox', element: f.onMapElements.infoBox});
754		}
755	} 
756	if (obj.stayC) {
757		var stayC = obj.stayC;
758		var stay = stayC.stay;
759		var fromArrowInfo = stay.fromArrowInfo;
760		var toArrowInfo = stay.toArrowInfo;
761		var f = stayC.feature;
762		var rect = stay.yearShowRect;
763		f.onMapElements.pointOnMap.removeClass('cop_eu_map_location_point');
764		f.onMapElements.pointOnMap.addClass('cop_eu_map_location_point_hover');
765		f.onMapElements.locationLabelHalo.show();
766		f.onMapElements.locationLabel.show();
767		stay.text.show();
768		this.addShowingElements({type: 'timeRect', element: rect});
769		this.addShowingElements({type: 'timeText', element: stay.yearShowFromText});
770		this.addShowingElements({type: 'timeText', element: stay.yearShowToText});
771		if (fromArrowInfo) {
772			this.addShowingElements({type: 'arrow', element: fromArrowInfo});
773		}
774		this.addShowingElements({type: 'arrow', element: toArrowInfo});
775		if (stayC.stay.clicked) {
776			this.addShowingElements({type: 'infoBox', element: f.onMapElements.infoBox});
777		}
778
779	}
780};
781
782
783/**
784 * 
785 * @param {object} obj {feature: feature, stayC: stayC}
786 */
787CopEuMap.prototype.unhightlightMapElements = function (obj) {
788	var _this = this;
789
790	if (obj.feature) {
791		var f = obj.feature;
792		if (!f.clicked) {
793			var innerStayClicked = false;
794
795			for (var i = 0; i < f.onMapElements.stays.length; i++) {
796				var stay = f.onMapElements.stays[i];
797				var rect = stay.yearShowRect;
798				if (stay.clicked === true) { 
799					innerStayClicked = true;
800				} else {
801					stay.text.hide();
802					this.removeShowingElements({type: 'timeRect', element: rect});
803					this.removeShowingElements({type: 'timeText', element: stay.yearShowFromText});
804					this.removeShowingElements({type: 'timeText', element: stay.yearShowToText});
805
806					if (!_this.isShowArrowLayer) {
807						var fromArrowInfo = stay.fromArrowInfo;
808						var toArrowInfo = stay.toArrowInfo;
809						var fromArrowFoundInClickedFeature = false;
810						var toArrowFoundInClickedFeature = false;
811						var fromArrowFoundInClickedStay = false;
812						var toArrowFoundInClickedStay = false;
813						// find from and to arrows related to this feature
814						for (var j = 0; j < this.staysList.length; j++) {
815							var tempStay = this.staysList[j];
816							if (tempStay !== stay && tempStay.clicked) {
817								if (fromArrowInfo === tempStay.toArrowInfo) {
818									fromArrowFoundInClickedStay = true;
819								}
820								if (toArrowInfo === tempStay.fromArrowInfo) {
821									toArrowFoundInClickedStay = true;
822								}
823							}
824						}
825						for (var j = 0; j < this.copJson.features.length; j++) {
826							var tempF = this.copJson.features[j];
827							if (tempF !== f && tempF.clicked) {
828								for (var k = 0; k < tempF.onMapElements.stays.length; k++) {
829									var tempStay = tempF.onMapElements.stays[k];
830									if (fromArrowInfo === tempStay.toArrowInfo) {
831										fromArrowFoundInClickedFeature = true;
832										break;
833									}
834								}
835								for (var k = 0; k < tempF.onMapElements.stays.length; k++) {
836									var tempStay = tempF.onMapElements.stays[k];
837									if (toArrowInfo === tempStay.fromArrowInfo) {
838										toArrowFoundInClickedFeature = true;
839										break;
840									}
841								}
842								
843							}
844						}
845						if (fromArrowInfo && !fromArrowFoundInClickedFeature && !fromArrowFoundInClickedStay) {
846							this.removeShowingElements({type: 'arrow', element: fromArrowInfo});
847						}
848						if (!toArrowFoundInClickedFeature && !toArrowFoundInClickedStay) {
849							this.removeShowingElements({type: 'arrow', element: toArrowInfo});
850						}
851					}
852				}
853			}
854
855			// when no stay in this city is clicked
856			if (!innerStayClicked) {
857				f.onMapElements.pointOnMap.removeClass('cop_eu_map_location_point_hover');
858				f.onMapElements.pointOnMap.addClass('cop_eu_map_location_point');
859				if (!this.isShowCityname) {
860					f.onMapElements.locationLabelHalo.hide();
861					f.onMapElements.locationLabel.hide();
862				}
863				this.removeShowingElements({type: 'infoBox', element: f.onMapElements.infoBox});
864			}
865
866
867		}
868	}
869	if (obj.stayC) {
870		var stayC = obj.stayC;
871		var stay = stayC.stay;
872		var fromArrowInfo = stay.fromArrowInfo;
873		var toArrowInfo = stay.toArrowInfo;
874		var f = stayC.feature;
875		
876		if (!stay.clicked) {
877			// console.log(stayC.stay.fromLocation);
878			// console.log(stayC);
879			stay.text.hide();
880			var rect = stay.yearShowRect;
881			this.removeShowingElements({type: 'timeRect', element: rect});
882			this.removeShowingElements({type: 'timeText', element: stay.yearShowFromText});
883			this.removeShowingElements({type: 'timeText', element: stay.yearShowToText});
884			// find from and to arrows related to this feature
885			var fromArrowFoundInClickedStay = false;
886			var toArrowFoundInClickedStay = false;
887			var foundInFeatureInClickedStay = false;
888			for (var j = 0; j < this.staysList.length; j++) {
889				var tempStayC = this.staysList[j];
890				if (tempStayC !== stayC && tempStayC.stay.clicked) {
891					if (fromArrowInfo === tempStayC.stay.toArrowInfo) {
892						fromArrowFoundInClickedStay = true;
893					}
894					if (toArrowInfo === tempStayC.stay.fromArrowInfo) {
895						toArrowFoundInClickedStay = true;
896					}
897					// find if this clicked stay using the same feature
898					if (f === tempStayC.feature)
899						foundInFeatureInClickedStay = true;
900				}
901			}
902			if (!this.isShowArrowLayer && fromArrowInfo && !fromArrowFoundInClickedStay) {
903				this.removeShowingElements({type: 'arrow', element: fromArrowInfo});
904			}
905			if (!this.isShowArrowLayer && !toArrowFoundInClickedStay) {
906				this.removeShowingElements({type: 'arrow', element: toArrowInfo});
907			}
908			if (!foundInFeatureInClickedStay) {
909				f.onMapElements.pointOnMap.removeClass('cop_eu_map_location_point_hover');
910				f.onMapElements.pointOnMap.addClass('cop_eu_map_location_point');
911				if (!this.isShowCityname) {
912					f.onMapElements.locationLabelHalo.hide();
913					f.onMapElements.locationLabel.hide();
914				}
915				this.removeShowingElements({type: 'infoBox', element: f.onMapElements.infoBox});
916			}
917
918		}
919	}
920
921};
922
923/**
924 * 
925 * @param {object} obj {feature: feature, stayC: stayC}
926 */
927CopEuMap.prototype.clickElements = function (obj) {
928	var _this = this;
929
930	if (obj.feature) {
931		var f = obj.feature;
932		f.clicked = !f.clicked;
933		
934		if (f.clicked) {
935			_this.hightlightMapElements({feature: f});
936			for (var i = 0; i < this.copJson.features.length; i++) {
937				var tempF = this.copJson.features[i];
938				// find clicked feature, set other features unclicked
939				if (tempF !== f) {
940					tempF.clicked = false;
941					for (j = 0; j < tempF.onMapElements.stays.length; j++){
942						tempF.onMapElements.stays[j].clicked = false;
943					}
944					_this.unhightlightMapElements({feature: tempF});
945					
946				} 
947			}
948		} else {
949			for (var i = 0; i < f.onMapElements.stays.length; i++){
950				f.onMapElements.stays[i].clicked = false;
951			}
952			this.currentStayNum = -1;
953			_this.unhightlightMapElements({feature: f});
954		}
955			
956	}
957	if (obj.stayC) { // click from time layer
958		var stayC = obj.stayC;
959		if (stayC.feature.clicked) { // do nothing if feature of stay is clicked
960			return;
961		} else {
962			for (var i = 0; i < this.copJson.features.length; i++) {
963				var tempF = this.copJson.features[i];
964				// find clicked feature and set to unclicked
965				if (tempF.clicked) {
966					tempF.clicked = false;
967					
968				} 
969			}
970		}
971		stayC.stay.clicked = !stayC.stay.clicked;
972		if (stayC.stay.clicked) {
973			var f = stayC.feature;
974			_this.hightlightMapElements({stayC: stayC});
975			for (var i = 0; i < _this.staysList.length; i++) {
976				if (_this.staysList[i] === stayC) {
977					_this.currentStayNum = i;
978					break;
979				}
980			}
981			
982			for (var i = 0; i < _this.staysList.length; i++) {
983				if (_this.staysList[i] !== stayC) {
984					_this.staysList[i].stay.clicked = false;
985					if (_this.staysList[i].feature !== stayC.feature){
986						stayC.feature.clicked = false;
987					}
988					_this.unhightlightMapElements({stayC: _this.staysList[i]});
989				}
990			}
991		} else {
992			this.currentStayNum = -1;
993			_this.unhightlightMapElements({stayC: stayC});
994		}
995		
996
997	}
998};
999
1000CopEuMap.prototype.addShowingElements = function (obj) {
1001	var type = obj.type;
1002	var element = obj.element;
1003	var found = false;
1004	for (var i = 0; i < this.showingElements.length; i ++) {
1005		if (this.showingElements[i] === element) {
1006			found = true;
1007			break;
1008		}
1009	}
1010	if (!found) {
1011		this.showingElements.push(element);	
1012		switch (type) {
1013			case 'arrow':
1014
1015				element.arrow.finish();
1016				element.arrow.animate(this.animationDuration).attr('opacity', 1);
1017				break;
1018		
1019			case 'timeRect':
1020				element.animate(this.animationDuration).attr('opacity', 0.7);
1021				break;
1022		
1023			case 'timeText':
1024				element.animate(this.animationDuration).attr('opacity', 1);
1025				break;
1026		
1027			case 'infoBox':
1028				element.fadeIn({
1029					duration: this.sidebarAnimationDuration, 
1030					done: function(){
1031					}
1032				});
1033				break;
1034		
1035			default:
1036				break;
1037		}
1038	}
1039
1040};
1041
1042CopEuMap.prototype.removeShowingElements = function (obj) {
1043	var type = obj.type;
1044	var element = obj.element;
1045	var found = false;
1046	var index = 0;
1047	for (var i = 0; i < this.showingElements.length; i ++) {
1048		if (this.showingElements[i] === element) {
1049			found = true;
1050			index = i;
1051			break;
1052		}
1053	}
1054	if (found && index != 0) {
1055		this.showingElements.splice(index, 1);	
1056		switch (type) {
1057			case 'arrow':
1058				element.arrow.animate(this.animationDuration/4).attr('opacity', 0);
1059				break;
1060		
1061			case 'timeRect':
1062				element.animate(this.animationDuration).attr('opacity', 0);
1063				break;
1064		
1065			case 'timeText':
1066				element.animate(this.animationDuration).attr('opacity', 0);
1067				break;
1068		
1069			case 'infoBox':
1070				element.fadeOut({
1071					duration: this.sidebarAnimationDuration, 
1072					done: function(){
1073					}
1074				});
1075				break;
1076		
1077			default:
1078				break;
1079		}
1080	}
1081
1082};
1083
1084
1085
1086CopEuMap.prototype.drawLayerControlElements = function () {
1087	var _this = this;
1088	var d = this.mapDrawarea;
1089	var mapBorderW = $('#cop_eu_map_map').css('stroke-width');
1090	mapBorderW = parseInt(mapBorderW.split('px')[0]);
1091	var boxX = this.gapToMapBorder + this.mapBoundariesW - 0*mapBorderW/2;
1092	var boxY = this.gapToMapBorder + this.mapBoundariesW - 0*mapBorderW/2;
1093	this.patternBG.attr('x', this.patternBG.attr('x')-0*mapBorderW/2);
1094	this.patternBG.attr('y', this.patternBG.attr('y')-0*mapBorderW/2);
1095	var cotrolBorder = 10;
1096	var boxW = 300;
1097	var boxH = 145;
1098	var rowGap = 25;
1099	var checkXGap = 5;
1100	var layerControlBox = d.rect(boxW, boxH).x(boxX).y(boxY).addClass('cop_eu_map_layer_
1100control_box').attr('fill', 'url(#cop-europamap-bgImage)');
1101	var rowX = this.mapBoundariesW + cotrolBorder * 1;
1102	var rowY = this.mapBoundariesW + cotrolBorder * 1;
1103
1104	var layerControlTitle = d.text('Legende').addClass('cop_eu_map_layer_control_box_text cop_eu_map_layer_control_box_text_header');
1105	var lctW = layerControlTitle.bbox().width, lctH = layerControlTitle.bbox().height;
1106	layerControlTitle.x((rowX+this.mapBoundariesW)/2+(boxW-lctW)/2).y(rowY);
1107
1108	rowY = rowY + rowGap;
1109
1110	// checkbox for city name text
1111	var r = this.pointRadiusBase;
1112	var cityPoint = d.circle(r).x(rowX).y(rowY+2).addClass('cop_eu_map_location cop_eu_map_location_point');
1113	var citynameText = d.text('Caspar Olevians Aufenthaltsorte').addClass('cop_eu_map_layer_control_box_text cop_eu_map_layer_control_box_text_cityname').x(rowX+cityPoint.bbox().width+cotrolBorder).y(rowY);
1114	var cntextW = citynameText.bbox().width, cntH = citynameText.bbox().height;
1115	var cnCheckXGroup = d.group(cntH, cntH).x(rowX+(boxW-cotrolBorder*2-cntH)).y(rowY);
1116	var cnCheckX1 = d.path().addClass('cop_eu_map_layer_control_box_checkX')
1117			.plot('M'+checkXGap+','+checkXGap+' '+
1118			(cntH-checkXGap)+','+(cntH-checkXGap));
1119	cnCheckXGroup.add(cnCheckX1);
1120	var cnCheckX2 = d.path().addClass('cop_eu_map_layer_control_box_checkX')
1121			.plot('M'+checkXGap+','+(cntH-checkXGap)+' '+
1122			(cntH-checkXGap)+','+checkXGap);
1123	cnCheckXGroup.add(cnCheckX2);
1124	// cnCheckXGroup.hide();
1125
1126	var cnCheck = d.rect(cntH, cntH).x(rowX+(boxW-cotrolBorder*2-cntH)).y(rowY).rx(5).ry(5).addClass('cop_eu_map_layer_control_box_check cop_eu_map_layer_control_box_check_cityname');
1127	
1128	// add event to cnCheck
1129	cnCheck.click(function (evt) {
1130		var features = _this.copJson['features'];
1131
1132		for(var i = 0; i < features.length; i++) {
1133			var f = features[i];
1134			if (!_this.isShowCityname) {
1135				f.onMapElements.locationLabelHalo.show();
1136				f.onMapElements.locationLabel.show();
1137			} else {
1138				f.onMapElements.locationLabelHalo.hide();
1139				f.onMapElements.locationLabel.hide();
1140			}
1141		}
1142
1143		if (!_this.isShowCityname) {
1144			cnCheckXGroup.show();
1145			_this.isShowCityname = true;
1146		} else {
1147			cnCheckXGroup.hide();
1148			_this.isShowCityname = false;
1149		}
1150	});
1151
1152	rowY = rowY + rowGap;
1153
1154	// checkbox for big cities
1155	var r = this.pointRadiusBase;
1156	var bigCityPoint = d.circle(r).x(rowX).y(rowY+2).addClass('cop_eu_map_location cop_eu_map_location_big_city_point');
1157	var bigCiText = d.text('Europäische Zentren').addClass('cop_eu_map_layer_control_box_text cop_eu_map_layer_control_box_text_big_cities').x(rowX+bigCityPoint.bbox().width+cotrolBorder).y(rowY);
1158	var bigCitextW = bigCiText.bbox().width, bigCitextH = bigCiText.bbox().height;
1159	var bigCiCheckXGroup = d.group(bigCitextH, bigCitextH).x(rowX+(boxW-cotrolBorder*2-bigCitextH)).y(rowY);
1160	var bigCiCheckX1 = d.path().addClass('cop_eu_map_layer_control_box_checkX')
1161			.plot('M'+checkXGap+','+checkXGap+' '+
1162			(bigCitextH-checkXGap)+','+(bigCitextH-checkXGap));
1163	bigCiCheckXGroup.add(bigCiCheckX1);
1164	var bigCiCheckX2 = d.path().addClass('cop_eu_map_layer_control_box_checkX')
1165			.plot('M'+checkXGap+','+(bigCitextH-checkXGap)+' '+
1166			(bigCitextH-checkXGap)+','+checkXGap);
1167	bigCiCheckXGroup.add(bigCiCheckX2);
1168	bigCiCheckXGroup.hide();
1169	var bigCiCheck = d.rect(bigCitextH, bigCitextH).x(rowX+(boxW-cotrolBorder*2-bigCitextH)).y(rowY).rx(5).ry(5).addClass('cop_eu_map_layer_control_box_check cop_eu_map_layer_control_box_check_bigCi');
1170	// add event to bigCiCheck
1171	bigCiCheck.click(function (evt) {
1172		// var features = _this.copJson['features'];
1173
1174
1175		for(var i = 0; i < _this.copBigCities.features.length; i++) {
1176			var f = _this.copBigCities.features[i];
1177			if (!_this.isShowBigCi) {
1178				f.onMapElements.locationLabelHalo.show();
1179				f.onMapElements.locationLabel.show();
1180				f.onMapElements.pointOnMapBigCi.show();
1181			} else {
1182				f.onMapElements.locationLabelHalo.hide();
1183				f.onMapElements.locationLabel.hide();
1184				f.onMapElements.pointOnMapBigCi.hide();
1185			}
1186		}
1187
1188		if (!_this.isShowBigCi) {
1189			bigCiCheckXGroup.show();
1190			_this.isShowBigCi = true;
1191		} else {
1192			bigCiCheckXGroup.hide();
1193			_this.isShowBigCi = false;
1194		}
1195	});
1196
1197	rowY = rowY + rowGap;
1198
1199	// checkbox for luther cities
1200	var r = this.pointRadiusBase;
1201	var lutherCityPoint = d.circle(r).x(rowX).y(rowY+2).addClass('cop_eu_map_location cop_eu_map_location_luther_city_point');
1202	var lutherCiText = d.text('Martin Luthers Aufenthaltsorte').addClass('cop_eu_map_layer_
1202control_box_text cop_eu_map_layer_control_box_text_luther_cities').x(rowX+lutherCityPoint.bbox().width+cotrolBorder).y(rowY);
1203	var lutherCiTextW = lutherCiText.bbox().width, lutherCiTextH = lutherCiText.bbox().height;
1204	var lutherCiCheckXGroup = d.group(lutherCiTextH, lutherCiTextH).x(rowX+(boxW-cotrolBorder*2-lutherCiTextH)).y(rowY);
1205	var lutherCiCheckX1 = d.path().addClass('cop_eu_map_layer_control_box_checkX')
1206			.plot('M'+checkXGap+','+checkXGap+' '+
1207			(lutherCiTextH-checkXGap)+','+(lutherCiTextH-checkXGap));
1208	lutherCiCheckXGroup.add(lutherCiCheckX1);
1209	var lutherCiCheckX2 = d.path().addClass('cop_eu_map_layer_control_box_checkX')
1210			.plot('M'+checkXGap+','+(lutherCiTextH-checkXGap)+' '+
1211			(lutherCiTextH-checkXGap)+','+checkXGap);
1212	lutherCiCheckXGroup.add(lutherCiCheckX2);
1213	lutherCiCheckXGroup.hide();
1214	var lutherCiCheck = d.rect(lutherCiTextH, lutherCiTextH).x(rowX+(boxW-cotrolBorder*2-lutherCiTextH)).y(rowY).rx(5).ry(5).addClass('cop_eu_map_layer_control_box_check cop_eu_map_layer_control_box_check_lutherCi');
1215	// add event to lutherCiCheck
1216	lutherCiCheck.click(function (evt) {
1217		// var features = _this.copJson['features'];
1218
1219
1220		for(var i = 0; i < _this.lutherCities.features.length; i++) {
1221			var f = _this.lutherCities.features[i];
1222			if (!_this.isShowLutherCi) {
1223				f.onMapElements.locationLabelHalo.show();
1224				f.onMapElements.locationLabel.show();
1225				f.onMapElements.pointOnMapLutherCi.show();
1226			} else {
1227				f.onMapElements.locationLabelHalo.hide();
1228				f.onMapElements.locationLabel.hide();
1229				f.onMapElements.pointOnMapLutherCi.hide();
1230			}
1231		}
1232
1233		if (!_this.isShowLutherCi) {
1234			lutherCiCheckXGroup.show();
1235			_this.isShowLutherCi = true;
1236		} else {
1237			lutherCiCheckXGroup.hide();
1238			_this.isShowLutherCi = false;
1239		}
1240	});
1241
1242	rowY = rowY + rowGap;
1243
1244	// checkbox for arrows
1245	var arrowHeadW = 5;
1246	var arrowMarkerInControl = d.marker(arrowHeadW, arrowHeadW, function(add){
1247		var p = add.path('M0,0 L0,'+arrowHeadW+' L'+arrowHeadW/4*3+' '+arrowHeadW/2+' z').addClass('cop_eu_map_location_arrow_head');
1248	}).attr('refX', arrowHeadW/3).attr('refY', arrowHeadW/2);
1249	var arrowPathW = lutherCityPoint.bbox().width;
1250	var arrowPath = d.path('M '+rowX+' '+(rowY+lutherCiText.bbox().height/2)+' l '+arrowPathW+' '+0).addClass('cop_eu_map_location_arrow').attr('opacity', 1).marker('end', arrowMarkerInControl).attr('stroke-width', this.pointRadiusBase/6);
1251	var arrowLayerText = d.text('Caspar Olevians Weg in Europa').addClass('cop_eu_map_layer_control_box_text cop_eu_map_layer_control_box_text_arrow').x(rowX+arrowPath.bbox().width+cotrolBorder).y(rowY);
1252	var arrowLayertextW = arrowLayerText.bbox().width, arrowLayertextH = arrowLayerText.bbox().height;
1253	var arrowLayerCheckXGroup = d.group(arrowLayertextH, arrowLayertextH).x(rowX+(boxW-cotrolBorder*2-arrowLayertextH)).y(rowY);
1254	var arrowLayerCheckX1 = d.path().addClass('cop_eu_map_layer_control_box_checkX')
1255			.plot('M'+checkXGap+','+checkXGap+' '+
1256			(arrowLayertextH-checkXGap)+','+(arrowLayertextH-checkXGap));
1257	arrowLayerCheckXGroup.add(arrowLayerCheckX1);
1258	var arrowLayerCheckX2 = d.path().addClass('cop_eu_map_layer_control_box_checkX')
1259			.plot('M'+checkXGap+','+(arrowLayertextH-checkXGap)+' '+
1260			(arrowLayertextH-checkXGap)+','+checkXGap);
1261	arrowLayerCheckXGroup.add(arrowLayerCheckX2);
1262	arrowLayerCheckXGroup.hide();
1263	var arrowLayerCheck = d.rect(arrowLayertextH, arrowLayertextH).x(rowX+(boxW-cotrolBorder*2-arrowLayertextH)).y(rowY).rx(5).ry(5).addClass('cop_eu_map_layer_control_box_check cop_eu_map_layer_control_box_check_arrowLayer');
1264	// add event to arrowLayerCheck
1265	arrowLayerCheck.click(function (evt) {
1266		// var features = _this.copJson['features'];
1267
1268		// for(var i = 0; i < _this.copJson.features.length; i++) {
1269		// 	var f = _this.copJson.features[i];
1270		// 	if (!_this.isShowArrowLayer) {
1271		// 		_this.showAllArrows(f);
1272		// 	} else {
1273		// 		if (!f.clicked) {
1274		// 			_this.hideAllArrows(f);
1275		// 		}
1276		// 	}
1277		// }
1278
1279		if (!_this.isShowArrowLayer) {
1280			_this.showAllArrows();
1281			arrowLayerCheckXGroup.show();
1282			_this.isShowArrowLayer = true;
1283		} else {
1284			_this.hideAllArrows();
1285			arrowLayerCheckXGroup.hide();
1286			_this.isShowArrowLayer = false;
1287		}
1288	});
1289	
1290	rowY = rowY + rowGap;
1291
1292	// // checkbox for bg image
1293	// var bgLayerText = d.text('farbige Karte: ').addClass('cop_eu_map_layer_control_box_text cop_eu_map_layer_control_box_text_arrow').x(rowX).y(rowY);
1294	// var bgLayerTextW = bgLayerText.bbox().width, bgLayerTextH = bgLayerText.bbox().height;
1295	// var bgLayerCheckXGroup = d.group(bgLayerTextH, bgLayerTextH).x(rowX+(boxW-cotrolBorder*2-bgLayerTextH)).y(rowY);
1296	// var bgLayerCheckX1 = d.path().addClass('cop_eu_map_layer_control_box_checkX')
1297	// 		.plot('M'+checkXGap+','+checkXGap+' '+
1298	// 		(bgLayerTextH-checkXGap)+','+(bgLayerTextH-checkXGap));
1299	// bgLayerCheckXGroup.add(bgLayerCheckX1);
1300	// var bgLayerCheckX2 = d.path().addClass('cop_eu_map_layer_control_box_checkX')
1301	// 		.plot('M'+checkXGap+','+(bgLayerTextH-checkXGap)+' '+
1302	// 		(bgLayerTextH-checkXGap)+','+checkXGap);
1303	// bgLayerCheckXGroup.add(bgLayerCheckX2);
1304	// bgLayerCheckXGroup.hide();
1305	// var bgLayerCheck = d.rect(bgLayerTextH, bgLayerTextH).x(rowX+(boxW-cotrolBorder*2-bgLayerTextH)).y(rowY).rx(5).ry(5).addClass('cop_eu_map_layer_control_box_check cop_eu_map_layer_control_box_check_bgLayer');
1306	// // add event to bgLayerCheck
1307	// bgLayerCheck.click(function (evt) {
1308	// 	// var features = _this.copJson['features'];
1309
1310
1311	// 	if (!_this.isShowbgLayer) {
1312	// 		_this.showColorBg();
1313	// 	} else {
1314	// 		_this.hideColorBg();
1315	// 	}
1316
1317	// 	if (!_this.isShowbgLayer) {
1318	// 		bgLayerCheckXGroup.show();
1319	// 		_this.isShowbgLayer = true;
1320	// 	} else {
1321	// 		bgLayerCheckXGroup.hide();
1322	// 		_this.isShowbgLayer = false;
1323	// 	}
1324	// });
1325	
1326	// rowY = rowY + rowGap;
1327
1328	// // checkbox for scale
1329	// var scaleLayerText = d.text('Lon/Lat: ').addClass('cop_eu_map_layer_control_box_text cop_eu_map_layer_control_box_text_arrow').x(rowX).y(rowY);
1330	// var scaleLayerTextW = scaleLayerText.bbox().width, scaleLayerTextH = scaleLayerText.bbox().height;
1331	// var scaleLayerCheckXGroup = d.group(scaleLayerTextH, scaleLayerTextH).x(rowX+(boxW-cotrolBorder*2-scaleLayerTextH)).y(rowY);
1332	// var scaleLayerCheckX1 = d.path().addClass('cop_eu_map_layer_control_box_checkX')
1333	// 		.plot('M'+checkXGap+','+checkXGap+' '+
1334	// 		(scaleLayerTextH-checkXGap)+','+(scaleLayerTextH-checkXGap));
1335	// scaleLayerCheckXGroup.add(scaleLayerCheckX1);
1336	// var scaleLayerCheckX2 = d.path().addClass('cop_eu_map_layer_control_box_checkX')
1337	// 		.plot('M'+checkXGap+','+(scaleLayerTextH-checkXGap)+' '+
1338	// 		(scaleLayerTextH-checkXGap)+','+checkXGap);
1339	// scaleLayerCheckXGroup.add(scaleLayerCheckX2);
1340	// // scaleLayerCheckXGroup.hide();
1341	// var scaleLayerCheck = d.rect(scaleLayerTextH, scaleLayerTextH).x(rowX+(boxW-cotrolBorder*2-scaleLayerTextH)).y(rowY).rx(5).ry(5).addClass('cop_eu_map_layer_control_box_check cop_eu_map_layer_control_box_check_scaleLayer');
1342	// // add event to scaleLayerCheck
1343	// scaleLayerCheck.click(function (evt) {
1344	// 	// var features = _this.copJson['features'];
1345
1346
1347	// 	if (!_this.isShowscaleLayer) {
1348	// 		_this.showScale();
1349	// 	} else {
1350	// 		_this.hideScale();
1351	// 	}
1352
1353	// 	if (!_this.isShowscaleLayer) {
1354	// 		scaleLayerCheckXGroup.show();
1355	// 		_this.isShowscaleLayer = true;
1356	// 	} else {
1357	// 		scaleLayerCheckXGroup.hide();
1358	// 		_this.isShowscaleLayer = false;
1359	// 	}
1360	// });
1361	
1362	// rowY = rowY + rowGap;
1363
1364};
1365
1366
1367CopEuMap.prototype.drawTimeControlElements = function () {
1368	var _this = this;
1369	var d = this.mapDrawarea;
1370	var boxW = this.timeControlWidth;
1371	var boxH = this.containerHeight;
1372	var boxX = 2 * this.mapBoundariesW + this.mapWidth;
1373	var boxY = 0;
1374	var timeControlBox = d.rect(boxW, boxH).x(boxX).y(boxY).addClass('cop_eu_map_time_control_box');
1375	var buttonW = 0;
1376	var buttonStartX = 0;
1377	var buttonBottom = 0;
1378
1379	// timeline
1380	this.yearScaleH = boxH / 60;  // 1536 - 1587
1381	var yearScaleTopGap = this.yearScaleH * 2.5;
1382	var yearScaleY1 = yearScaleTopGap;
1383	var yearScaleFontSize = this.yearScaleH / 4 * 3;
1384	for (var year = 1535; year < 1591; year ++ ) {
1385		var yearScaleStrokeW = 1;
1386		var yearScaleW = boxW / 3;
1387		var yearText = '';
1388		if (year % 10 === 0) {
1389			yearScaleW = boxW / 2;
1390			yearScaleStrokeW = 2;
1391			yearText = '' + year;
1392			var yearScaleX1 = boxX + yearScaleW/4;
1393			// var yearScaleX4 = yearScaleX1 + yearScaleW + boxW - yearScaleW / 4;
1394			var yearScaleX4 = boxX + boxW - yearScaleW/4;
1395
1396			var tempYearText = d.text(yearText).addClass('cop_eu_map_year_text').attr('font-size', yearScaleFontSize+'px');
1397			tempYearText.x(boxX + (boxW - tempYearText.bbox().width) / 2).y(yearScaleY1-tempYearText.bbox().height / 2);
1398
1399			var yearScaleX2 = tempYearText.x() - 4;
1400			var yearScaleX3 = tempYearText.x() + tempYearText.bbox().width + 4;
1401			// var tempYearScale = d.line(yearScaleX1, yearScaleY1, yearScaleX2, yearScaleY1).stroke({width: yearScaleStrokeW}).addClass('cop_eu_map_year_scale');
1402			// var tempYearScale = d.line(yearScaleX3, yearScaleY1, yearScaleX4, yearScaleY1).stroke({width: yearScaleStrokeW}).addClass('cop_eu_map_year_scale');
1403			buttonW = yearScaleX4 - yearScaleX1;
1404			buttonStartX = yearScaleX1;
1405
1406		} else {
1407			var yearScaleX1 = boxX + 0.4*boxW;
1408			var yearScaleX2 = boxX + 0.6*boxW;
1409			var tempYearScale = d.line(yearScaleX1, yearScaleY1, yearScaleX2, yearScaleY1).stroke({width: yearScaleStrokeW}).addClass('cop_eu_map_year_scale');
1410
1411
1412		}
1413
1414		yearScaleY1 += this.yearScaleH;
1415	}
1416
1417	buttonBottom = yearScaleY1 - this.yearScaleH;
1418
1419	// create arrow time description data structure
1420	var features = this.copJson.features;
1421	for(var i = 0; i < features.length; i++) {
1422		var f = features[i];
1423		var stays = f.onMapElements.stays;
1424		for(var j = 0; j < stays.length; j++) {
1425			this.staysList.push({feature: f, stay: stays[j]});
1426		}
1427	}
1428	this.staysList.sort(function (a, b) {
1429		if (a.stay.date.isBefore(b.stay.date)) {
1430			return -1;
1431		} 
1432		if (b.stay.date.isBefore(a.stay.date)) {
1433			return 1;
1434		} 
1435		return 0;
1436	});
1437
1438	// console.log(this.staysList);
1439
1440	// draw durations on time control frame
1441	
1442	for (var i = 0; i < this.staysList.length; i++) {
1443		var fromArrowDesc = null;
1444		if (i > 0) {
1445			fromArrowDesc = this.staysList[i-1];
1446		}
1447		var stayC = this.staysList[i];
1448		var f = this.staysList[i].feature;
1449		var nextStayC = null;
1450		var toDate = null;
1451		var date = stayC.stay.date;
1452		if (i < this.staysList.length - 1) {
1453			nextStayC = this.staysList[i+1];
1454			toDate = nextStayC.stay.date;
1455		} else {
1456			toDate = moment('1587-03-15').locale('de');
1457		}
1458		if (i === 0) {
1459			yearScaleY1 = yearScaleTopGap + this.yearScaleH * date.diff(moment('1535-01-01'), 'days')/365.0;
1460		}
1461		var duration = toDate.diff(date, 'days') / 365.0;
1462		var yearShowRectX = boxX;
1463		var yearShowRectY = yearScaleY1;
1464		var yearShowRectW = boxW;
1465		var yearShowRectH = this.yearScaleH * duration;
1466		
1467		var tempYearShowRect = d.rect(yearShowRectW, yearShowRectH).x(yearShowRectX).y(yearShowRectY).addClass('cop_eu_map_year_show_rect')
1468		.attr('opacity', 0)
1469		;
1470		//var yearShowFromText = d.text(date.year()+'.'+(date.month()+1)).addClass('cop_eu_map_year_show_text_from').attr('font-size', yearScaleFontSize+'px');
1471		var yearShowFromText = d.text((date.month()+1)+'/'+date.year()).addClass('cop_eu_map_year_show_text_from').attr('font-size', yearScaleFontSize+'px');
1472		yearShowFromText.x(yearShowRectX).y(yearShowRectY-yearShowFromText.bbox().height);
1473		yearShowFromText.attr('opacity', 0);
1474		// var yearShowToText = d.text(toDate.year()+'.'+(toDate.month()+1)).addClass('cop_eu_map_year_show_text_from').attr('font-size', yearScaleFontSize+'px');
1475		var yearShowToText = d.text((toDate.month()+1)+'/'+toDate.year()).addClass('cop_eu_map_year_show_text_from').attr('font-size', yearScaleFontSize+'px');
1476		yearShowToText.x(yearShowRectX+yearShowRectW-yearShowToText.bbox().width).y(yearShowRectY+yearShowRectH);
1477		yearShowToText.attr('opacity', 0);
1478
1479		(function (stayC) {
1480			tempYearShowRect.mouseover(function (evt) {
1481				_this.hightlightMapElements({stayC: stayC});
1482			});
1483			tempYearShowRect.mouseout(function (evt) {
1484				_this.unhightlightMapElements({stayC: stayC});
1485			});
1486			tempYearShowRect.click(function (evt) {
1487				_this.clickElements({stayC: stayC});
1488			});
1489		})(stayC);
1490		stayC.stay.yearShowRect = tempYearShowRect;
1491		stayC.stay.yearShowFromText = yearShowFromText;
1492		stayC.stay.yearShowToText = yearShowToText;
1493
1494		yearScaleY1 += yearShowRectH;
1495	}
1496
1497	// last button
1498	var lastButtonStartX = buttonStartX;
1499	var lastButtonStartY = this.yearScaleH * 1.5;
1500	var lastButtonMiddleX = lastButtonStartX + buttonW / 2;
1501	var lastButtonMiddleY = this.yearScaleH * 0.5;
1502	var lastButtonEndX = lastButtonMiddleX + buttonW / 2;
1503	var lastButtonEndY = lastButtonStartY;
1504	var lastButtonPath = d.path('M '+lastButtonStartX+' '+lastButtonStartY+' L '+lastButtonMiddleX+' '+lastButtonMiddleY+' L '+lastButtonEndX+' '+lastButtonEndY).addClass('cop_eu_map_year_last_button');
1505	lastButtonPath.click(function (evt) {
1506		_this.lastStay();
1507	});
1508	// next button
1509	var nextButtonStartX = buttonStartX;
1510	var nextButtonStartY = this.yearScaleH * 1 + buttonBottom;
1511	var nextButtonMiddleX = nextButtonStartX + buttonW / 2;
1512	var nextButtonMiddleY = this.yearScaleH * 2 + buttonBottom;
1513	var nextButtonEndX = nextButtonMiddleX + buttonW / 2;
1514	var nextButtonEndY = nextButtonStartY;
1515	var nextButtonPath = d.path('M '+nextButtonStartX+' '+nextButtonStartY+' L '+nextButtonMiddleX+' '+nextButtonMiddleY+' L '+nextButtonEndX+' '+nextButtonEndY).addClass('cop_eu_map_year_last_button');
1516	nextButtonPath.click(function (evt) {
1517		_this.nextStay();
1518	});
1519
1520};
1521
1522CopEuMap.prototype.addInfoElements = function () {
1523	var _this = this;
1524	var d = this.mapDrawarea;
1525	var infoBoxW = this.containerWidth - this.mapContainerWidth;
1526	var infoBoxH = this.containerHeight;
1527	var infoBoxX = this.mapContainerWidth;
1528	var infoBoxY = 0;
1529
1530	this.infoBoxSidebar = $('<div class="cop_eu_map_info_sidebar"></div>');
1531	$('#'+this.containerId).append(this.infoBoxSidebar);
1532	this.infoBoxSidebar.width(infoBoxW).height(infoBoxH);
1533	var titleBlockH = Math.ceil(this.containerHeight/10);
1534
1535	for (var i = 0; i < this.copJson.features.length; i ++) {
1536		var f = this.copJson.features[i];
1537		// add text div
1538		var tempInfoBox = $('<div class="cop_eu_map_info_box"></div>');
1539		tempInfoBox.width(infoBoxW).height(infoBoxH);
1540		this.infoBoxSidebar.append(tempInfoBox);
1541		var location = f.properties.location; 
1542		var titleBlock = $('<div class="cop_eu_map_info_box_title"></div>');
1543		titleBlock.width(infoBoxW);
1544		var titleDiv = $('<div class="cop_eu_map_info_box_titleText">'+location+'</div>');
1545		titleDiv.width(infoBoxW);
1546		titleBlock.append(titleDiv);
1547		tempInfoBox.append(titleBlock);
1548		titleBlockH = titleBlock.height();
1549		var textAllDiv = $('<div class="cop_eu_map_info_textAll"></div>');
1550		textAllDiv.width(infoBoxW).height(infoBoxH - titleBlockH);
1551		tempInfoBox.append(textAllDiv);
1552
1553		for (var j = 0; j < f.onMapElements.stays.length; j++) {
1554			var stay = f.onMapElements.stays[j];
1555			var nextStayC = null;
1556			var toDate = null;
1557			var date = stay.date;
1558			// find date to next stay
1559			for (var k = 0; k < this.staysList.length; k++) {
1560				if (this.staysList[k].stay === stay) {
1561					if (k < this.staysList.length - 1) {
1562						nextStayC = this.staysList[k+1];
1563						toDate = nextStayC.stay.date;
1564					} else {
1565						toDate = moment('1587-03-15').locale('de');
1566					}
1567					break;
1568				}
1569			}
1570			if (date.format('MMM YYYY') === toDate.format('MMM YYYY')) {
1571			   var showStrTimeOrt = date.format('MMMM YYYY');
1572			} else {
1573			   var showStrTimeOrt = date.format('MMMM YYYY') + ' bis ' + toDate.format('MMMM YYYY');
1574			}
1575			var showStrDesc = stay.description;
1576			var descDiv = $('<div class="cop_eu_map_info_text"><p/><p class="cop_eu_map_info_text_timeTitle">'+showStrTimeOrt+'</p>'+
1577				'<p class="cop_eu_map_info_text_desc">'+showStrDesc+'</p></div>');
1578			descDiv.width(infoBoxW);
1579			textAllDiv.append(descDiv);
1580			stay.text = descDiv;
1581			descDiv.hide();
1582		}
1583		tempInfoBox.hide();
1584		f.onMapElements.infoBox = tempInfoBox;
1585	}
1586
1587};
1588
1589CopEuMap.prototype.lastStay = function () {
1590
1591	this.currentStayNum--;
1592
1593	if (this.currentStayNum < -1) {
1594		this.currentStayNum = -1;
1595	} else if (this.currentStayNum === -1) {
1596		this.clickElements({stayC: this.staysList[0]});
1597	} else if (this.currentStayNum === this.staysList.length) {
1598		this.clickElements({stayC: this.staysList[this.staysList.length-1]});
1599	} {
1600		this.clickElements({stayC: this.staysList[this.currentStayNum]});
1601	} 
1602};
1603
1604CopEuMap.prototype.nextStay = function () {
1605
1606	this.currentStayNum++;
1607
1608	if (this.currentStayNum > this.staysList.length) {
1609		this.currentStayNum = this.staysList.length;
1610	} else if (this.currentStayNum === this.staysList.length) {
1611		this.clickElements({stayC: this.staysList[this.staysList.length-1]});
1612	} else {
1613		this.clickElements({stayC: this.staysList[this.currentStayNum]});
1614	}
1615};
1616
1617
1618CopEuMap.prototype.getUrlParameter = function (sParam) {
1619    var sPageURL = decodeURIComponent(window.location.search.substring(1)),
1620        sURLVariables = sPageURL.split('&'),
1621        sParameterName,
1622        i;
1623
1624    for (i = 0; i < sURLVariables.length; i++) {
1625        sParameterName = sURLVariables[i].split('=');
1626
1627        if (sParameterName[0] === sParam) {
1628            return sParameterName[1] === undefined ? true : sParameterName[1];
1629        }
1630    }
1631};
1632
1633/**
1634 *  not used
1635 */
1636CopEuMap.prototype.convertDateToNum = function (date) {
1637
1638	var year = parseInt(date.split('-')[0]);
1639	var month = parseInt(date.split('-')[1]);
1640	var day = parseInt(date.split('-')[2]);
1641
1642	// do not consider feb and the 31th day
1643	var days = (month - 1) * 30 + day;
1644
1645	return year + days / 360.0;
1646
1647};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.