
if (typeof naviki == "undefined") {
	var naviki = {}
}
if (typeof naviki.util == "undefined") {
	naviki.util = {}
}

/**
 * depends on
 * - naviki.util.geo
 * - naviki.util.second
 * - naviki.util.user
 */
naviki.util.wayduration = {

	/**
	 * This method adds the travel duration to the given element.
	 * @param durationInSeconds in seconds
	 * @param pathInfoTravelTimeElement the view element
	 */
	addDuration: function (durationInSeconds, pathInfoTravelTimeElement) {
		if (durationInSeconds > 0) {
			const time = naviki.util.second.toTime(durationInSeconds) + " " + naviki.Lang.getLL('base.hour');
			pathInfoTravelTimeElement.html(time);
			pathInfoTravelTimeElement.show();
		} else {
			pathInfoTravelTimeElement.hide();
		}
	},

	/**
	 * This method returns the duration of a path in seconds and takes the default speed
	 * of the selected profile and the user's average speed into consideration.
	 * @param duration The path duration in seconds
	 * @param routingProfileName
	 */
	getNormalizedDuration: function(duration, routingProfileName, routingServer) {
		var defaultSpeed = 20.0;
		var denominator;
		if (naviki.util.user.isLoggedIn()) {
			var userProfileSpeed = naviki.util.user.getUserAverageSpeed();
			denominator = (userProfileSpeed / defaultSpeed) > 0.0 ? (userProfileSpeed / defaultSpeed) : 1.0;
		} else {
			var server = this.getRoutingServerByName(routingProfileName, routingServer);
			var routingServerSpeed = server != null && server.defaultSpeed > 0.0 ? server.defaultSpeed : defaultSpeed;
			denominator = (routingServerSpeed / defaultSpeed) > 0.0 ? (routingServerSpeed / defaultSpeed) : 1.0;
		}
		return Math.round(duration * (1.0 / denominator));
	},

	calcDurationInSeconds: function (path, recordedPathDuration, routingServer) {
		return this.calcDurationInSecondsImpl(path.routeInstructions, path.routingProfile, path.km, recordedPathDuration, routingServer);
	},

	/**
	 * calc duration of way in seconds, use instructions or given recorded path duration
	 *
	 * @param routeInstructions instructions array
	 * @param routingProfile profile name as string
	 * @param pathLength in km
	 * @param recordedPathDuration in seconds
	 * @param routingServer list of available routing server
	 */
	calcDurationInSecondsImpl: function (routeInstructions, routingProfile, pathLength, recordedPathDuration, routingServer) {
		if (routeInstructions != null && routeInstructions.length > 5) {
			var durationInSeconds = 0;
			var instr = JSON.parse(routeInstructions);
			for (var i = 0; i < instr.length; i++) {
				durationInSeconds += instr[i][4];
			}
			durationInSeconds = this.getNormalizedDuration(durationInSeconds, routingProfile, routingServer);
			return durationInSeconds;
		} else if (recordedPathDuration != null && recordedPathDuration > 0) {
			return recordedPathDuration;
		} else if ((recordedPathDuration == null || recordedPathDuration <= 0) && pathLength > 0) {
			return Math.round((pathLength / naviki.util.user.getUserAverageSpeed()) * 3600.0);
		} else {
			return 0;
		}
	},

	/**
	 * Returns a server by given name.
	 * e.g. {uid: 41, routingUrl: "r.naviki.org/5030", profileName: "daily", frontendName: "Alltag", useForRoundTrip: 1}
	 *
	 * @returns {Object}
	 */
	getRoutingServerByName: function(name, routingServer) {
		if (routingServer != null) {
			for (var index = 0; index < routingServer.length; index++) {
				if (routingServer[index].profileName === name) {
					return routingServer[index];
				}
			}
		}
		return null;
	}
}
if (typeof naviki == "undefined") {
	var naviki = {}
}
if (typeof naviki.util == "undefined") {
	naviki.util = {}
}

naviki.util.way = {

	isCalculated: function(path) {
		return path.wayType == naviki.CONSTANTS.WAY_TYPE.wayTypeRoutingRequest
			|| path.wayType == naviki.CONSTANTS.WAY_TYPE.wayTypeRoundtrip
			|| path.wayType == naviki.CONSTANTS.WAY_TYPE.wayTypeMatchedPath;
	},

	isOwnRecordOrUploadedAsRecorded: function(path) {
		return this.isCurrentUserWayOwner(path)
			&& path.waySource == naviki.CONSTANTS.WAY_SOURCE.waySourceRealPath
			&& (path.wayType == naviki.CONSTANTS.WAY_TYPE.wayTypeRecordedPath
				|| path.wayType == naviki.CONSTANTS.WAY_TYPE.wayTypeUploadedPath);
	},

	isCurrentUserWayOwner: function(path) {
		return path.feUserid && naviki.util.user.getUserId() && path.feUserid == naviki.util.user.getUserId();
	},

	isOsmRelation: function(path) {
		return path.wayType == naviki.CONSTANTS.WAY_TYPE.wayTypeOsmRelation;
	},

	getViaPoints: function(path) {
		return path.viaPoints;
	},

	createWayTypeIconElement: function(path, calculatedIconEnabled) {
		const iconElement = $('<span/>', {});
		this.updateWayTypeIconElement(path, iconElement, calculatedIconEnabled);
		return iconElement;
	},

	updateWayTypeIconElement: function(path, iconElement, calculatedIconEnabled) {
		iconElement.removeClass(); // remove all classes
		iconElement.attr('title', '');

		if (path.osmRelationsId > 0) {
			iconElement.addClass("icon-routes-confirmed");
			iconElement.attr('title', naviki.Lang.getLL("tx_naviki_pimapcontrol.official.tip"));
		} else {
			if (this.isCalculated(path) && calculatedIconEnabled) {
				iconElement.addClass("icon-routes-calculated");
				iconElement.attr('title', naviki.Lang.getLL("pi_portlet.allWays.button.filter.calculated"));
			} else if (this.isOwnRecordOrUploadedAsRecorded(path)) {
				iconElement.addClass("icon-routes-cycled");
				iconElement.attr('title', naviki.Lang.getLL("pi_portlet.allWays.button.filter.recorded"));
			} else {
				// do not show any icon (empty span element)
			}
		}
	},

	completeDestinations(destinations) {
		// update ui after running a bunch of name service requests (NAV-6531)
		var inWorkCount = 0;
		var anyUpdateTitleOnAddressComplete = false;
		var anyGeoFieldComplete = false;
		const callback = function(geoFieldComplete, updateTitleOnAddressComplete) {
			if (updateTitleOnAddressComplete) anyUpdateTitleOnAddressComplete = true;
			if (geoFieldComplete) anyGeoFieldComplete = true;

			inWorkCount--;

			if (inWorkCount % 20 == 0) {
				$(document).trigger("destinationAddressFieldReady", {force: false});
				
				if (inWorkCount == 0 && anyGeoFieldComplete) {
					$(document).trigger("destinationGeoFieldReady");
				}
			}
		}

		for (var index = 0; index < destinations.length; index++) {
			const dst = destinations[index];
			if (dst.isGeoFieldEmpty() || dst.isAddressFieldEmpty()) {
				inWorkCount++;
				dst.complete(callback);
			}
		}
	}
}
if (typeof naviki == "undefined") {
	var naviki = {}
}

if (typeof naviki.util == "undefined") {
	naviki.util = {}
}

naviki.util.api = {

	PrivateWayState: {
		publicWay: 0,
		privateWay: 1,
		sharedWay: 2
	},

	isTokenRefresh: false,
	ajaxRequestStack: new Array(),

	getAjaxDispatcherBasePath: function() {
		return "/" + naviki.CONSTANTS.LANGUAGE + "/naviki/ajax-dispatcher/";
	},
	
	setAccessToken: function(accessToken) {
		if(typeof(localStorage) !== "undefined") {	
			window.localStorage.setItem("_n_a_at", accessToken);
		}
	},
		
	getAccessToken: function() {
		if(typeof(localStorage) !== "undefined") {	
			var accessToken = window.localStorage.getItem("_n_a_at");
			if(accessToken != "undefined" && accessToken != null) {
				return accessToken;
			}
		}
		return null;
	},
		
	removeAccessToken: function() {
		if(typeof(localStorage) !== "undefined") {	
			window.localStorage.removeItem("_n_a_at");
		}
	},
		
	setRefreshToken: function(refreshToken) {
		if(typeof(localStorage) !== "undefined") {	
			window.localStorage.setItem("_n_a_rt", refreshToken);
		}
	},
		
	getRefreshToken: function() {
		if(typeof(localStorage) !== "undefined") {	
			var refreshToken = window.localStorage.getItem("_n_a_rt");
			if(refreshToken != "undefined" && refreshToken != null) {
				return refreshToken;
			}
		}
		return null;
	},
		
	removeRefreshToken: function() {
		if(typeof(localStorage) !== "undefined") {	
			window.localStorage.removeItem("_n_a_rt");
		}
	},
	
	request: function(options) {
		var parentBeforeSend = null;
		if (options.beforeSend != undefined && typeof options.beforeSend === "function") {
			parentBeforeSend = options.beforeSend;
		}
		options.beforeSend = function(xhr) {
			if (parentBeforeSend != null && typeof parentBeforeSend === "function") {
	        	parentBeforeSend();
	        }
			if (naviki.util.api.getAccessToken() != null) {
				xhr.setRequestHeader('Authorization', 'Bearer ' + naviki.util.api.getAccessToken());
			}
			if (typeof(naviki.CONSTANTS.LOCALE_ALL) !== "undefined") {
				const locale = naviki.CONSTANTS.LOCALE_ALL.replace("_","-");
				xhr.setRequestHeader('Accept-Language', locale);
			}
	    }
		
		var parentError = null;
		if (options.error != undefined && typeof options.error === "function") {
			parentError = options.error;
		}
		options.error = function(jqXHR, textStatus, errorThrown) {
			if (jqXHR != null && jqXHR.status != null && jqXHR.status == 401) {
				naviki.util.api.ajaxRequestStack.push(options);
				if (!naviki.util.api.isTokenRefresh) {
					naviki.util.api.isTokenRefresh = true;
					naviki.util.api.refreshToken();
				}
				return;
			}
			if (parentError != null && typeof parentError === "function") {
				parentError(jqXHR);
	        }
		}
		
		return $.ajax(options);
	},
	
	refreshToken: function() {
		var params = "?request[format]=json&request[controller]=FeUser&request[action]=refreshToken";
		params += "&request[arguments][refreshToken]=" + naviki.util.api.getRefreshToken();

		$.ajax({
			url: naviki.util.api.getAjaxDispatcherBasePath() + encodeURI(params),
			type: "GET",
			cache: false,
			dataType : "json",
			contentType: 'application/json; charset=utf-8',
		    processData: false,
			success : function(data) {					
				if (data.status && data.accessToken && data.refreshToken) {
					naviki.util.api.setAccessToken(data.accessToken);
					naviki.util.api.setRefreshToken(data.refreshToken);
					naviki.util.user.setUserId(data.feUserId); // could be missing, if last login long ago
					while(naviki.util.api.ajaxRequestStack.length > 0) {
						$.ajax(naviki.util.api.ajaxRequestStack.pop());
					}
					naviki.util.api.ajaxRequestStack = new Array();
					naviki.util.api.isTokenRefresh = false;
				} else {
					naviki.util.api.logoutFeUser();
				}
			},
			error: function(err) {
				naviki.util.api.logoutFeUser();
			}
		});
	},
	
	logoutFeUser: function() {
		naviki.util.api.removeAccessToken();
		naviki.util.api.removeRefreshToken();

		naviki.util.user.setUserProfile(null);
		naviki.util.user.setUsername(null);
		naviki.util.user.setUserId(null);
		naviki.util.user.setActivated(false);
		naviki.util.user.setUserAverageSpeed(null);
		naviki.util.user.setUserEnergyUnit(null);
		naviki.util.user.setUserLengthUnit(null);
		naviki.util.user.setUserWayFilters(null);
		naviki.util.user.setUserWaySort(null);

		naviki.util.contest.clear();
	},

	updateUserLanguage: function(language, callback) {
		const userDataObject = {
			uid: naviki.util.user.getUserId(),
			username: naviki.util.user.getUsername(),
			language: language
		};
		this.updateUserProfile(userDataObject, callback);
	},

	updateUserLengthUnit: function(lengthUnit) {
		const userDataObject = {
			uid: naviki.util.user.getUserId(),
			username: naviki.util.user.getUsername(),
			lengthUnit: lengthUnit
		};
		this.updateUserProfile(userDataObject, function() {});
	},

	updateUserProfile: function(userDataObject, callback) {
		if (!naviki.util.user.isLoggedIn()) {
			if (callback) {
				callback();
			}
			return;
		}
		const url = encodeURI(naviki.CONSTANTS.API_BASE_URL) + "User/2/update/";
		naviki.util.api.request({
			url: url,
			type: 'PUT',
			cache: false,
			contentType: 'application/json; charset=utf-8',
			data: JSON.stringify(userDataObject),
			error: function(errorData) {
				console.log(errorData);
			},
			complete: callback
		});
	},

	loadUserProfile: function() {
		if (!naviki.util.user.isLoggedIn()) {
			return;
		}
		const url = encodeURI(naviki.CONSTANTS.API_BASE_URL) + "User/2/find/?package=org.naviki";
		naviki.util.api.request({
			url: url,
			type: 'GET',
			cache: false,
			dataType: "json",
			contentType: 'application/json; charset=utf-8',
			success: function (data) {
				naviki.util.user.setUserProfile(data);
				naviki.util.user.setUserLengthUnit(data.lengthUnit);
				naviki.util.user.setUserEnergyUnit(data.energyUnit);
				console.log("Loaded user profile.");

				// check and force update of length unit, it may still be set "undefined" in database
				naviki.util.localeformatter.getCurrentUserLengthUnit();
			},
			error: function (errorData) {
				console.log(errorData);
			}
		});
	},

	validateAndUpdateWayPrivacy(path, callback) {
		if (!naviki.util.user.isLoggedIn() || path.privateWay != this.PrivateWayState.privateWay || path.uid < 1) {
			if (callback) {
				callback();
			}
			return;
		}

		path.privateWay = this.PrivateWayState.sharedWay;

		const updateData = { uid: path.uid, privateWay: path.privateWay };
		this.updateWay(updateData, callback);
	},

	updateWay: function(wayDataObject, callback) {
		const url = encodeURI(naviki.CONSTANTS.API_BASE_URL) + "Way/update/" + wayDataObject.uid + "/";
		naviki.util.api.request({
			url: url,
			type: 'PUT',
			cache: false,
			contentType: 'application/json; charset=utf-8',
			data: JSON.stringify(wayDataObject),
			error: function(errorData) {
				console.log(errorData);
			},
			complete: callback
		});
	},

	getFindWayApiUrl: function(uuid, uid) {
		if (uuid == null && uid != null) {
			const apiV5BaseUrl = window.location.protocol + "//" + window.location.host + "/naviki/api/v5/";
			return encodeURI(apiV5BaseUrl + "Way/find/" + uid + "/");
		}

		const apiCall = naviki.util.user.isLoggedIn() ? "find/withUser/" : "find/anonymous/";
		return encodeURI(naviki.CONSTANTS.API_BASE_URL + "Way/2/" + apiCall + uuid + "/");
	}
}
if (typeof naviki == "undefined") {
	var naviki = {}
}

if (typeof naviki.util == "undefined") {
	naviki.util = {}
}

naviki.util.binary = {

	prepare : function(binary, length) {
		if (binary.length < length) {
			do {
				binary = "0" + binary;
			} while (binary.length < length);
		}
		return "" + binary;
	},

	getBit : function(binary, pos) {
		binary = naviki.util.prepareBinary("" + binary, pos + 1);
		tmp = binary.split("");
		return tmp[tmp.length - (pos + 1)];
	},

	setBit : function(binary, pos, value) {
		binary = naviki.util.prepareBinary("" + binary, pos + 1);
		tmp = binary.split("");
		tmp[tmp.length - (pos + 1)] = value;
		binary = "";
		for (var i = 0; i < tmp.length; i++) {
			binary += tmp[i];
		}
		return binary;
	}
}
if (typeof naviki == "undefined") {
	var naviki = {}
}
if (typeof naviki.util == "undefined") {
	naviki.util = {}
}

naviki.util.contest = {

	data: null,

	loadContest: function(contestId, callback) {
		if (this.data != null) {
			callback(this.data);
			return;
		}

		const _self = this;
		const url = encodeURI(naviki.CONSTANTS.API_BASE_URL + "Contest/findContest/" + contestId + "/");
		naviki.util.api.request({
			url : url,
			type : "GET",
			cache: false,
			dataType : "json",
			contentType: 'application/json; charset=utf-8',
			success : function(data) {
				if (!data || !data.contest) {
					return;
				}
				_self.data = data;
				callback(_self.data);
			}
		});
	},

	clear: function() {
		this.data = null;
	}
}
if(typeof naviki == "undefined"){
	var naviki = {}
}

if(typeof naviki.util == "undefined"){
	naviki.util = {}
}

naviki.util.compression = {
		
		// Encodes one-dimensional and two-dimensional arrays.
		//
		// @param array decodedObject
		//
		// e.g. decodedObject = array: ((1,2,3,4),(5,6,7,8),(9,10,11,12),(13,14,15,16))
		//		result = "ACEGGGGGGGGGGGGG"
		//
		//		decodedObject = array: (1,2,3,4)
		//		result = "AAAA"
		//
		encode: function(decodedObject)
		{
		    var encodedString = "";
		    var prevRow;
		    var row;
		    
		    var delta, prevVal = 0;
		    
		    for(var i = 0; i < decodedObject.length; i++)
		    {
		        if($.isArray(decodedObject[i]))
		        {
		            row = decodedObject[i];
		            
		            // If is the first row, than
		            // generate a previous row with 0 values​​.
		            if(i == 0)
		            {
		                prevRow = new Array();
		                for(var k = 0; k < row.length; k++)
		                {
		                    prevRow.push(0);
		                }
		            }
		            
		            for(var j = 0; j < row.length; j++)
		            {
		                // Calculate the delta from the previous value.
		                delta = parseInt(row[j]) - parseInt(prevRow[j]);
		                
		                // Encode the delta value.
		                encodedString = naviki.util.compression.encodeValue(encodedString,delta);
		            }
		            // Hold the current row.
		            prevRow = row;
		        }
		        else
		        {
		            // Calculate the delta from the previous value.
		            delta = parseInt(decodedObject[i]) - parseInt(prevVal);
		            // Hold the current value.
		            prevVal = parseInt(decodedObject[i]);
		            // Encode the delta value.
		            encodedString = naviki.util.compression.encodeValue(encodedString,delta);
		        }
		    }
		    
		    return encodedString;
		},

		encodeValue: function(encodedString,delta)
		{
		    // Left-shift the value one bit.
		    // If the original decimal value is negative, invert this encoding.
		    delta = (delta < 0) ? ~(delta << 1) : (delta << 1);

		    // OR each value with 0x20 if another 5-bit chunk follows.
		    // Add 63 to each value.
		    // Convert each value to its ASCII equivalent.
		    while (delta >= 0x20) {
		    	var val = (0x20 | (delta & 0x1f)) + 63;
		        encodedString += String.fromCharCode(val);
		        delta >>= 5;
		    }
		    encodedString += String.fromCharCode(delta + 63);
		    return encodedString;
		},
		
		// Decodes an encoded string.
		//
		// @param string encodedString The encoded string.
		// @param int columnCount Number of values ​​per row.
		//
		// e.g. encodedString = "ACEGGGGGGGGGGGGG"
		//		columnCount = 4
		//		result = array: ((1,2,3,4),(5,6,7,8),(9,10,11,12),(13,14,15,16))
		//
		//		encodedString = "AAAA"
		//		columnCount = 1
		//		result = array: (1,2,3,4)
		//
		decode: function(encodedString,columnCount)
		{
		    var length = encodedString.length;
		    var idx = 0;
		    
		    var byte;
		    var shift;
		    var delta;
		    var value;
		    var res;
		    
		    var result = new Array();
		    var prevRow;
		    var row;
		    
		    while (idx < length)
		    {
		        // If is the first row, than
		        // generate a previous row with 0 values​​.
		        if(result.length == 0)
		        {
		            prevRow = new Array();
		            for(var i = 0; i < columnCount; i++)
		            {
		                prevRow.push(0);
		            }
		        }
		  
		        row = new Array();
		        for(var i = 0; i < columnCount; i++)
		        {
		            byte  = 0;
		            res   = 0;
		            shift = 0;

		            do {
		                // The statement returns the ASCII code for the character at index.
		                // Subtract 63 to get the original value.
		                // (63 was added to ensure proper ASCII characters are displayed
		                // in the encoded polyline string, which is `human` readable)
		                byte = encodedString.charCodeAt(idx++) - 63;
		                // AND the bits of the byte with 0x1f to get the original 5-bit `chunk.
		                // Then left shift the bits by the required amount, which increases
		                // by 5 bits each time.
		                // OR the value into results, which sums up the individual 5-bit chunks
		                // into the original value.  Since the 5-bit chunks were reversed in
		                // order during encoding, reading them in this way ensures proper
		                // summation.
		                res |= (byte & 0x1F) << shift;
		                shift += 5;
		                // Continue while the read byte is >= 0x20 since the last `chunk`
		                // was not OR'd with 0x20 during the conversion process. (Signals the end)
		            } while (byte >= 0x20);
		     
		            // Check if negative, and convert. (All negative values have the last bit set)
		            delta = (res & 1) ? ~(res >> 1) : (res >> 1);
		            
		            // Summing the delta to the previous value.
		            value = parseInt(prevRow[i]);
		            value += delta;
		            
		            row.push(value);
		        }
		        result.push(row);
		        // Hold the current row.
		        prevRow = row;
		    }

		    // if the column count 1, than return a one-dimensional array.
		    if(columnCount == 1)
		    {
		        var change = new Array();
		        for(var i = 0; i < result.length; i++)
		        {
		            change.push(result[i][0]);
		        }
		        result = change;
		    }

		    return result;
		}
};
if (typeof naviki == "undefined") {
	var naviki = {}
}

if (typeof naviki.util == "undefined") {
	naviki.util = {}
}

naviki.util.cookie = {

	read : function(key) {
		var c_value = document.cookie;
		var c_start = c_value.indexOf(" " + key + "=");
		if (c_start == -1) {
			c_start = c_value.indexOf(key + "=");
		}
		if (c_start == -1) {
			c_value = null;
		} else {
			c_start = c_value.indexOf("=", c_start) + 1;
			var c_end = c_value.indexOf(";", c_start);
			if (c_end == -1) {
				c_end = c_value.length;
			}
			c_value = unescape(c_value.substring(c_start, c_end));
		}
		return c_value;
	},

	write : function(key, value) {
		var e = 1000 * 60 * 60 * 24 * 365;
		var a = new Date();
		a = new Date(a.getTime() + e);
		document.cookie = key + '=' + value + ';expires=' + a.toGMTString()
				+ ';path=/';
	},

	remove : function(key) {
		document.cookie = key + '=;expires=Thu, 01-Jan-70 00:00:01 GMT;path=/';
	}
}
if (typeof naviki == "undefined") {
	var naviki = {}
}

if (typeof naviki.util == "undefined") {
	naviki.util = {}
}

naviki.util.geo = {

	/**
	 * Calculates the distance between two points.
	 * 
	 * @param p1 Object that contains the geo-coordinates.
	 * @param p2 Object that contains the geo-coordinates.
	 * @return integer The distance in km.
	 */
	calculateDistance : function(p1, p2) {
		var radlat1 = Math.PI * p1.lat / 180;
		var radlat2 = Math.PI * p2.lat / 180;
		var radlon1 = Math.PI * p1.lng / 180;
		var radlon2 = Math.PI * p2.lng / 180;
		var theta = p1.lng - p2.lng;
		var radtheta = Math.PI * theta / 180;
		var dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1)
				* Math.cos(radlat2) * Math.cos(radtheta);
		dist = Math.acos(dist);
		dist = dist * 180 / Math.PI;
		dist = dist * 60 * 1.1515;
		dist = dist * 1.609344;

		if (!isNaN(dist)) {
			return dist;
		}

		return 0;
	},

	/**
	 * Calculate the bearing between two positions as a value from 0-360
	 *
	 * @param lat1 - The latitude of the first position
	 * @param lng1 - The longitude of the first position
	 * @param lat2 - The latitude of the second position
	 * @param lng2 - The longitude of the second position
	 *
	 * @return int - The bearing between 0 and 360
	 */
	calculateBearing : function(lat1, lng1, lat2, lng2) {
		var dLon = (lng2 - lng1);
		var y = Math.sin(dLon) * Math.cos(lat2);
		var x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1)
				* Math.cos(lat2) * Math.cos(dLon);
		var brng = Math.atan2(y, x) * 180 / Math.PI;
		return 360 - ((brng + 360) % 360);
	}
}
if(typeof naviki == "undefined"){
	var naviki = {}
}

if(typeof naviki.util == "undefined"){
	naviki.util = {}
}

naviki.util.googlepolyline = {
		
		/**
		 * Decodes a polyline that was encoded using the Google Maps method.
		 *
		 * The encoding algorithm is detailed here:
		 * http://code.google.com/apis/maps/documentation/polylinealgorithm.html
		 *
		 * This function is based off of Mark McClure's JavaScript polyline decoder
		 * (http://facstaff.unca.edu/mcmcclur/GoogleMaps/EncodePolyline/decode.js)
		 * which was in turn based off Google's own implementation.
		 *
		 * This function assumes a validly encoded polyline.  The behaviour of this
		 * function is not specified when an invalid expression is supplied.
		 *
		 * @param String encoded the encoded polyline.
		 * 
		 * @return Array an Nx2 array with the first element of each entry containing
		 *  the latitude and the second containing the longitude of the
		 *  corresponding point.
		 */
		decode : function(encoded, precision) {

			if (typeof precision == "undefined" || precision == null) {
				precision = 5;
			}
			
			var precisionMultiplier = Math.pow(10, -precision);
			var index = 0, lat = 0, lng = 0, array = [];
			var b, shift, result;
			var dlat,dlng;
			
			while (index < encoded.length) {
				// The encoded polyline consists of a latitude value followed by a
				// longitude value.  They should always come in pairs.  Read the
				// latitude value first.
				shift  = 0;
				result = 0;
				do {
					// The statement returns the ASCII code for the character at index.  
					// Subtract 63 to get the original value.
					// (63 was added to ensure proper ASCII characters are displayed
					// in the encoded polyline string, which is `human` readable)
					b = encoded.charCodeAt(index++) - 63;
					// AND the bits of the byte with 0x1f to get the original 5-bit `chunk.
					// Then left shift the bits by the required amount, which increases
					// by 5 bits each time.
					// OR the value into results, which sums up the individual 5-bit chunks
					// into the original value.  Since the 5-bit chunks were reversed in
					// order during encoding, reading them in this way ensures proper
					// summation.
					result |= (b & 0x1f) << shift;
					shift += 5;
					// Continue while the read byte is >= 0x20 since the last `chunk`
					// was not OR'd with 0x20 during the conversion process. (Signals the end)
				} while (b >= 0x20);
				// Check if negative, and convert. (All negative values have the last bit set)	
				dlat = ((result & 1) ? ~(result >> 1) : (result >> 1));
				// Compute actual latitude since value is offset from previous value.
				lat += dlat;
				
				// The next values will correspond to the longitude for this point.
				shift  = 0;
				result = 0;
				do {
					b = encoded.charCodeAt(index++) - 63;
					result |= (b & 0x1f) << shift;
					shift += 5;
				} while (b >= 0x20);
				dlng = ((result & 1) ? ~(result >> 1) : (result >> 1));
				lng += dlng;
				
				var latVal = (lat * precisionMultiplier).toFixed(precision);
				var lngVal = (lng * precisionMultiplier).toFixed(precision);
				array.push([latVal, lngVal]);
			}
			
			return array;
		},

		/**
		 * Encodes a polyline that was decoded using the Google Maps method.
		 * (https://developers.google.com/maps/documentation/utilities/polylinealgorithm?hl=de&csw=1)
		 */
		encode : function(points, precision){
			
			precision = Math.pow(10, precision);
			
			var dlat, dlng;
			var latE5,lngE5;
			var nextValue;
			var plat = 0, plng = 0;
			var encodedPoints = "";
			
			for(var i = 0; i < points.length; i++){	
				// Take the decimal value and multiply it by 1e5.
				latE5 =  parseInt(points[i][0] * precision);    
			    // Calculate the delta from the previous value.
			    dlat = latE5 - plat;	    
			    // Hold the current value.
			    plat = latE5;    
			    // Left-shift the value one bit.
			    dlat = dlat << 1;    
			    // If the original decimal value is negative, invert this encoding.
			    if (dlat < 0) {
			    	dlat = ~(dlat);
			    }    
			    // OR each value with 0x20 if another 5-bit chunk follows.
			    // Add 63 to each value.
			    // Convert each value to its ASCII equivalent.
				while (dlat >= 0x20) {
					nextValue = (0x20 | (dlat & 0x1f)) + 63;
					encodedPoints += String.fromCharCode(nextValue);
					dlat >>= 5;
				}
				encodedPoints += String.fromCharCode(dlat + 63);
				
				// The next values will correspond to the longitude for this point.
			    lngE5 = parseInt(points[i][1] * precision);
			    dlng = lngE5 - plng;
			    plng = lngE5;
			    dlng = dlng << 1;
			    
			    if (dlng < 0) {
			    	dlng = ~(dlng);
			    }
			    
			    while (dlng >= 0x20) {
					nextValue = (0x20 | (dlng & 0x1f)) + 63;
					encodedPoints += String.fromCharCode(nextValue);
					dlng >>= 5;
				}
				encodedPoints += String.fromCharCode(dlng + 63);
			}
			return encodedPoints;
		},
		
		mergeTracks : function(geom) {
			if(geom == null) {
				console.log("geom was null!");
				return [];
			}
			
			var points = new Array();
			for (var i = 0; i < geom.length; i++) {
				const pointList = naviki.util.googlepolyline.decode(geom[i], 5);
				for (var counter = 0; counter < pointList.length; counter++) {
					points.push([parseFloat(pointList[counter][0]), parseFloat(pointList[counter][1])]);
				}
			}
			return points;
		},
		
		reverseTrack: function(geom) {
			if(geom == null) {
				console.log("geom was null!");
				return;
			}
			const mergedTracks = naviki.util.googlepolyline.mergeTracks(geom);	
			return mergedTracks.reverse();
		},
		
		/**
		 * 
		 * @param encoded
		 * @param max
		 * @returns string encoded the encoded polyline
		 */
		reduceToMaxPoints : function(encoded, max){		
			var points = naviki.util.googlepolyline.decode(encoded, 5);
			if(points.length > max){
				var newArray = new Array();
				var m = parseInt((points.length / max) + 1);
				
				for(var i = 0; i < points.length; i++){
					if(parseInt((i % m)) == 0){
						newArray.push(points[i]);
					}
				}
				return naviki.util.googlepolyline.encode(newArray, 5);
			}
			return encoded;
		},
		
		
		turnDirection: function(encoded){
			var points = naviki.util.googlepolyline.decode(encoded, 5);
			var newArray = new Array();
			for(var i = (points.length - 1); i >= 0; i--){
				newArray.push(points[i]);
			}
			return naviki.util.googlepolyline.encode(newArray, 5);
		}
};
if (typeof naviki == "undefined") {
	var naviki = {}
}

if (typeof naviki.util == "undefined") {
	naviki.util = {}
}

naviki.util.html = {

	showMapboxUnsupportedDialog: function(closeCallback) {
		const content = '<b>' + naviki.Lang.getLL('pi_map.error.web_gl_disabled.title')+ '</b><br/>'
						+ naviki.Lang.getLL('pi_map.error.web_gl_disabled.msg');
		const dialog = new $.fn.navikiUiDialog({
			id: "naviki-ui-mapbox-support-dialog",
			content: content,
			className: "edit-dialog",
			closeCallback: closeCallback,
			buttons:[{
				text: naviki.Lang.getLL('base.ok'),
				callback: function() { dialog.close(); }
			}]
		});
		dialog.open();
	},

	/**
	 * The normal bring to top function does not work correctly 
	 * because of the nui- classes. So i must implement ower own.
	 * @returns {Number}
	 */
	bringToTop : function() {
		var aDivs = new Array();
		// get all div elements.
		aDivs = document.getElementsByTagName("div");
		if (aDivs.length == 0) {
			return 10;
		}
		var maxIndex = -9999;
		var count = aDivs.length;
		for (var i = 1; i < count; i++) {
			var div = aDivs[i];
			var sZIndex = Ext.get(div).getStyle("zIndex");
			var nZIndex = (!sZIndex || isNaN(sZIndex)) ? 0 : parseInt(sZIndex,
					10);
			if (nZIndex > maxIndex) {
				maxIndex = nZIndex;
			}
		}
		return maxIndex;
	},

	escapeHtml(unsafe) {
		// https://stackoverflow.com/a/6234804/5742907
		return unsafe
			.replace(/&/g, "&amp;")
			.replace(/</g, "&lt;")
			.replace(/>/g, "&gt;")
			.replace(/"/g, "&quot;")
			.replace(/'/g, "&#039;");
	},

	br2nl(input) {
		return input.replace(/<br\s*\/?>/mg,"\n");
	},

	nl2br(input) {
		return input.replace(/\n/g, "<br />");
	},

	/**
	 * Prepare text to display in html area:
	 * - convert <p>-paragraphs to <br>-tags
	 * - escape all html tags for secure output except of <br>-tags
	 * - general output all new lines as <br>
	 *
	 * @param text e.g. way descriptions
	 * @return string well-formatted text
	 */
	prepareTextForDisplay(text) {
		var result = this.replaceParagraphWithBreak(text);
		result = this.br2nl(result);
		result = this.escapeHtml(result);
		result = this.nl2br(result);
		return result;
	},

	/**
	 * Rreplace P tag with Br tag.
	 * @param str
	 * @returns
	 */
	replaceParagraphWithBreak : function(str) {
		str = str.toString();
		str = str.replace(/<p[^>]*?>/gi, "");
		return str.replace("</p>", "<br />");
	},

	wrapUrlsWithLinkTag : function(text) {
		const exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gi;
		return text.replace(exp,"<a href='$1' target='_blank' style='line-break: anywhere;'>$1</a>");
	},

	/**
	 * Substring not in HTLM tags.
	 * @param str
	 * @param len
	 * @returns
	 */
	substr : function(str, len) {
		var temp = str.substr(0, len);
		if (temp.lastIndexOf('<') > temp.lastIndexOf('>')) {
			temp = str.substr(0, 1 + str.indexOf('>', temp.lastIndexOf('<')));
		}
		return temp;
	},

	/**
	 * Cut text with use of "..." respecting complete words
	 *
	 * @param text input string
	 * @param limit max length
	 */
	substrWithEllipses: function(text, limit) {
		if (text.length > limit) {
			for (let i = limit; i > 0; i--) {
				if (text.charAt(i) === ' ' && (text.charAt(i-1) !== ',' || text.charAt(i-1) !== '.' || text.charAt(i-1) !== ';')) {
					return text.substring(0, i) + '...';
				}
			}
			return text.substring(0, limit) + '...';
		} else {
			return text;
		}
	}
}
if (typeof naviki == "undefined") {
	var naviki = {}
}

if (typeof naviki.util == "undefined") {
	naviki.util = {}
}

naviki.util.second = {

	toDate : function(timevalue) {
		var mydate = new Date();
		mydate.setTime(timevalue * 1000);
		var month = mydate.getMonth() + 1;
		if (month < 10) {
			month = "0" + month;
		}
		var day = mydate.getDate();
		if (day < 10) {
			day = "0" + day;
		}
		return day + "." + month + "." + mydate.getFullYear();
	},

	toDateAndTime : function(timevalue) {
		var mydate = new Date();
		mydate.setTime(timevalue * 1000);
		var month = mydate.getMonth() + 1;
		if (month < 10) {
			month = "0" + month;
		}
		var day = mydate.getDate();
		if (day < 10) {
			day = "0" + day;
		}
		var minutes = mydate.getMinutes();
		if (minutes < 10) {
			minutes = "0" + minutes;
		}
		var hours = mydate.getHours();
		if (hours < 10) {
			hours = "0" + hours;
		}
		return day + "." + month + "." + mydate.getFullYear() + ", " + hours
				+ ":" + minutes;
	},

	toTime : function(seconds) {
		var hours = Math.floor(seconds / (60 * 60));
		seconds -= hours * (60 * 60);
		var minutes = Math.floor(seconds / 60);
		if (minutes < 10) {
			minutes = "0" + minutes;
		}
		return hours + ":" + minutes;
	}
}
if(typeof naviki == "undefined"){
	var naviki = {}
}

if(typeof naviki.util == "undefined"){
	naviki.util = {}
}

naviki.util.solr = function(config) {
	this.config = config;
	this.initialize();
};

naviki.util.solr.prototype.initialize = function(){
	this.url = this.config.url;
	this.mainCore = this.config.mainCore;
	this.sharedCore = null;
	if(typeof this.config.sharedCore != "undefined" && this.config.sharedCore != null && this.config.sharedCore.length > 0){
		this.sharedCore = this.config.sharedCore;
	}
};

naviki.util.solr.prototype.getSearchURL = function(){
	this.config.url = naviki.util.url.replaceProtocol(this.config.url);
	return this.config.url + "/solr/" + this.mainCore + "/select";
};

naviki.util.solr.prototype.getReverseSearchURL = function(){
	this.config.url = naviki.util.url.replaceProtocol(this.config.url);
	return this.config.url + "/solr/" + this.mainCore + "/reverse";
};

naviki.util.solr.prototype.getJsonCallback = function(){
	return "json.wrf";
};

naviki.util.solr.prototype.getSearchRequestData = function(query,language,point){
	var result = {
			"q" : query,
			"qt" : "default",
			"language" : language
	};
	if (point != null) {
		result.pt = point.lat + "," + point.lng;
	}
	if(this.sharedCore != null){
		result.collection = this.sharedCore + "," + this.mainCore;
	}
	return result;
};

naviki.util.solr.prototype.getReverseSearchRequestData = function(latitude,longitude,language){
	var pt = latitude + "," + longitude;
	var result = {
			"q": "*:*",
			"fq": "{!geofilt}",
			"pt": pt,
			"d": "5",
			"format" : "json",
			"language" : language,
			"onlyAddress" : true,
			"rows": 1
		};
	if(this.sharedCore != null){
		result.collection = this.mainCore + "," + this.sharedCore;
	}
	return result;
};

naviki.util.solr.prototype.formatSearchResponse = function(response){
	var _self = this;
	var docs = response.response;
	if(response.response.docs){
		docs = response.response.docs;
	}
	var jsonResult = $.map(docs, function(jsonItem) {
		var item = _self.formatResponseItem(jsonItem);
		if(item != null){
			return item;	
		}
	});
	return jsonResult;
};

naviki.util.solr.prototype.formatReverseSearchResponse = function(response){
	var docs = response.response;
	if(response.response.docs){
		docs = response.response.docs;
	}
	if(docs.length == 0){
		return null;
	};
	return this.formatResponseItem(docs[0]);
};

naviki.util.solr.prototype.formatResponseItem = function(item){
		
	var coordinate = item.coordinate.split(",");
	
	var point = {
		lat : coordinate[0],
		lon : coordinate[1],
		icon : null,
		label: item.label,
		subLabel : item.subLabel
	};

	return point;
};

if (typeof naviki == "undefined") {
	var naviki = {}
}

if (typeof naviki.util == "undefined") {
	naviki.util = {}
}

naviki.util.url = {

	/**
	 * This method converts a query string to a object.
	 * e.g. 
	 * source -> ?id=10&type=20
	 * target -> {id: 10,type: 20}
	 * 
	 * @param query
	 * @returns {Object}
	 */
	decodeParams : function(query) {

		if (query.indexOf("?") == 0) {
			query = query.substr(1);
		}

		if (query.indexOf("#") == 0) {
			query = query.substr(1);
		}

		var params = {};
		var pairs = query.split('&');
		for (var i = 0; i < pairs.length; i++) {

			var pair = pairs[i].split('=');

			var accessors = [];
			var name = decodeURIComponent(pair[0]), value = decodeURIComponent(pair[1]);

			if (name.length == 0) {
				continue;
			}

			var name = name.replace(/\[([^\]]*)\]/g, function(k, acc) {
				accessors.push(acc);
				return "";
			});
			accessors.unshift(name);

			var obj = null;
			for (var j = accessors.length - 1; j >= 0; j--) {
				var acc = accessors[j];
				if (obj == null) {
					obj = {};
					obj[acc] = value;
				} else {
					var tmp = {};
					tmp[acc] = obj;
					obj = $.extend({}, tmp);
				}
			}
			params = (jQuery).extend(true, {}, params, obj);
		}

		return params;
	},

	/**
	 * This method converts a object to a query string.
	 * e.g.
	 * source -> {id: 10,type: 20}
	 * target -> ?id=10&type=20
	 * 
	 * @param params
	 * @returns
	 */
	encodeParams : function(params) {

		function encodeItem(params, key) {

			var query = "";

			if (typeof params == "object") {

				for (k in params) {

					if (query.length > 0) {
						query += "&";
					}

					query += encodeItem(params[k], key + "[" + k + "]");
				}
			} else {

				query += key + "=" + params;
			}

			return query;
		}
		;

		var query = "";
		var isFirst = true;

		for (key in params) {

			if (query.length > 0) {
				query += "&";
			}

			query += encodeItem(params[key], key);
		}

		return query;
	},

	/**
	 * This method replaces the protocol from a URL.
	 * 
	 * @param url
	 * @returns {String}
	 */
	replaceProtocol : function(url) {
		// do not replace scheme or port if server is "localhost" (e.g. for routing testing)
		if (url.indexOf("://localhost") != -1) {
			console.log("Skip replaceProtocol because it is a development/testing url.")
			return url;
		}

		var result;
		var protocol = window.location.protocol;

		if (url.search(protocol) == -1) {

			var parts = url.split("//");
			var path = parts[parts.length - 1];

			// If the protocol is http, then cut out the port.
			if (protocol == "http:" && path.search(/:/) > 0) {
				parts = path.split(":");
				result = protocol + "//" + parts[0];
				parts = path.split("/");
				for (var i = 1; i < parts.length; i++) {
					result += "/" + parts[i];
				}
			} else {
				result = protocol + "//" + path;
			}
		} else {
			result = url;
		}

		return result;
	},

	/**
	 * This method replaces a value in the url query.
	 * @param keyToReplace
	 * @param value
	 * @returns {String}
	 */
	replaceParam : function(keyToReplace, value) {

		var url = window.location.protocol + "//" + window.location.host
				+ window.location.pathname;
		var params = naviki.util.url.decodeParams(window.location.search);

		params[keyToReplace] = value;

		var query = naviki.util.url.encodeParams(params);
		if (query.length > 0) {
			url += "?" + query + window.location.hash;
		} else {
			url += window.location.hash;
		}

		return url;
	},

	/**
	 * This method removes a key from the url query.
	 * @param keyToRemove
	 * @returns {String}
	 */
	removeParam : function(keyToRemove) {

		var url = window.location.protocol + "//" + window.location.host
				+ window.location.pathname;
		var params = naviki.util.url.decodeParams(window.location.search);

		if (keyToRemove in params) {
			delete params[keyToRemove];
		}

		var query = naviki.util.url.encodeParams(params);
		if (query.length > 0) {
			url += "?" + query + window.location.hash;
		} else {
			url += window.location.hash;
		}

		return url;
	},
	
	/**
	 * This method removes keys from the url query.
	 * @param keyToRemove
	 * @returns {String}
	 */
	removeParams : function(keysToRemove) {

		var url = window.location.protocol + "//" + window.location.host
				+ window.location.pathname;
		var params = naviki.util.url.decodeParams(window.location.search);

		for (var key in params) {
			if(keysToRemove.indexOf(key) != -1) {  
				delete params[key];
			}		
		}

		var query = naviki.util.url.encodeParams(params);
		if (query.length > 0) {
			url += "?" + query + window.location.hash;
		} else {
			url += window.location.hash;
		}

		return url;
	},	

	/**
	 * This method gives back a value from the url query.
	 * @param key
	 * @returns
	 */
	extractValue : function(key) {
		var params = window.location.search, value = "";

		if (params.indexOf(key) < 0) {
			return null;
		}

		value = params.substring(params.indexOf(key), params.length);
		value = value.substring(key.length + 1,
				(value.indexOf("&") > -1) ? value.indexOf("&") : value.length);
		return value;
	},

	/**
	 * This method adds a slash to the end when it is missing.
	 * @param url
	 * @returns the formated url
	 */
	addSlash : function(url) {
		var pos = url.indexOf("/");
		if (pos != 0) {
			url = "/" + url;
		}
		return url;
	},
	
	/**
	 * This method gives back the domain from the url.
	 * @param url
	 * @returns
	 */
	extractDomain: function(url) {
	    var domain = url.replace(/(https?:\/\/)?(www.)?/i, '');
	    domain = domain.split('.');
	    domain = domain.slice(domain.length - 2).join('.');
	    if (domain.indexOf('/') !== -1) {
	        return domain.split('/')[0];
	    }
	    return domain;
	}
}
if (typeof naviki == "undefined") {
	var naviki = {}
}

if (typeof naviki.util == "undefined") {
	naviki.util = {}
}

naviki.util.localeformatter = {

	FACTOR_KCAL_KJ: 4.1868,
	FACTOR_KM_MILES: 0.621371,
	FACTOR_KMH_MPH: 0.621371,
	FACTOR_METER_FEET: 3.28084,

	ENERGY_UNIT_KCAL: 10,
	ENERGY_UNIT_KJ: 20,
	LENGTH_UNIT_UNDEFINED: 0,
	LENGTH_UNIT_METRIC: 10,
	LENGTH_UNIT_IMPERIAL: 20,

	getConvertedEnergy: function(energyInKilocalories) {
		const userEnergyUnit = naviki.util.user.getUserEnergyUnit();
		const factor = (userEnergyUnit != null && userEnergyUnit == this.ENERGY_UNIT_KJ) ? this.FACTOR_KCAL_KJ : 1.0;
		const resultValue = this.round(energyInKilocalories * factor, 0);
		return "" + resultValue + " " + this.getEnergyUnit();
	},

	getEnergyUnit: function() {
		const userEnergyUnit = naviki.util.user.getUserEnergyUnit();
		const isKj = (userEnergyUnit != null && userEnergyUnit == this.ENERGY_UNIT_KJ);
		return naviki.Lang.getLL("pi_userprofile.energy." + (isKj ? "kilojoule" : "kilocalories"));
	},

	getConvertedLength: function(lengthInKilometers, digits) {
		const convValue = this.getConvertedLengthValue(lengthInKilometers);
		const resultValue = this.round(convValue, digits);
		return "" + resultValue + " " + this.getLengthUnit();
	},

	getConvertedLengthValue: function(lengthInKilometers) {
		const factor = (this.getCurrentUserLengthUnit() == this.LENGTH_UNIT_IMPERIAL) ? this.FACTOR_KM_MILES : 1.0;
		return lengthInKilometers * factor;
	},

	getReverseConvertedLengthValue: function(convertedLength) {
		const factor = (this.getCurrentUserLengthUnit() == this.LENGTH_UNIT_IMPERIAL) ? this.FACTOR_KM_MILES : 1.0;
		return convertedLength / factor;
	},

	getLengthUnit: function() {
		const key = (this.getCurrentUserLengthUnit() == this.LENGTH_UNIT_IMPERIAL) ? "base.miles" : "base.kilometre";
		return naviki.Lang.getLL(key);
	},

	getConvertedDistance: function(distanceInMeters, digits) {
		const convValue = this.getConvertedDistanceValue(distanceInMeters);
		const resultValue = this.round(convValue, digits);
		return "" + resultValue + " " + this.getDistanceUnit();
	},

	getConvertedDistanceValue: function(distanceInMeters) {
		const factor = (this.getCurrentUserLengthUnit() == this.LENGTH_UNIT_IMPERIAL) ? this.FACTOR_METER_FEET : 1.0;
		return distanceInMeters * factor;
	},

	getDistanceUnit: function() {
		const key = (this.getCurrentUserLengthUnit() == this.LENGTH_UNIT_IMPERIAL) ? "base.feet" : "base.meter";
		return naviki.Lang.getLL(key);
	},

	getConvertedSpeed: function(speedInKmh, digits) {
		const convValue = this.getConvertedSpeedValue(speedInKmh);
		const resultValue = this.round(convValue, digits);
		return "" + resultValue + " " + this.getSpeedUnit();
	},

	getConvertedSpeedValue: function(speedInKmh) {
		const factor = (this.getCurrentUserLengthUnit() == this.LENGTH_UNIT_IMPERIAL) ? this.FACTOR_KMH_MPH : 1.0;
		return speedInKmh * factor;
	},

	getReverseConvertedSpeedValue: function(convertedSpeed) {
		const factor = (this.getCurrentUserLengthUnit() == this.LENGTH_UNIT_IMPERIAL) ? this.FACTOR_KMH_MPH : 1.0;
		return convertedSpeed / factor;
	},

	getSpeedUnit: function() {
		const key = (this.getCurrentUserLengthUnit() == this.LENGTH_UNIT_IMPERIAL) ? "base.miles.mih" : "base.kilometre.kmh";
		return naviki.Lang.getLL(key);
	},

	isImperial: function() {
		const userLengthUnit = this.getCurrentUserLengthUnit();
		return (userLengthUnit != null && userLengthUnit == this.LENGTH_UNIT_IMPERIAL);
	},

	getCurrentUserLengthUnit: function() {
		var userLengthUnit = naviki.util.user.getUserLengthUnit();

		// when users length unit has status undefined, try to get it locally and persist result to user per API
		// when it is null (still not loaded), do nothing, fallback to metric system
		if ((userLengthUnit == null && !naviki.util.user.isLoggedIn()) || (userLengthUnit != null && userLengthUnit == this.LENGTH_UNIT_UNDEFINED)) {
			userLengthUnit = this.getClientLengthUnit();
			naviki.util.user.setUserLengthUnit(userLengthUnit);
			naviki.util.api.updateUserLengthUnit(userLengthUnit);
		}

		if (userLengthUnit == null) {
			userLengthUnit = this.LENGTH_UNIT_METRIC;
		}

		return userLengthUnit;
	},

	getClientLengthUnit: function() {
		const countries = Array('US', 'LR', 'MM'); // Imperial measurement countries
		var location = window.navigator.language; // "de-DE"

		// If it is GoogleBot, do not trust on (missing) Accept-Language header, instead use page language
		// https://developers.google.com/search/docs/advanced/crawling/overview-google-crawlers?hl=de
		if (window.navigator.userAgent.indexOf("Googlebot") !== -1) { // contains
			if (naviki && naviki.CONSTANTS && naviki.CONSTANTS.LOCALE_ALL) {
				const pageLocale = naviki.CONSTANTS.LOCALE_ALL.replace("_","-"); // "de-DE"
				location = pageLocale;
			}
		}

		const country = location.substring(3, 5) // "DE"
		if (countries.indexOf(country) != -1) {
			return this.LENGTH_UNIT_IMPERIAL;
		}
		return this.LENGTH_UNIT_METRIC;
	},

	/**
	 * Rounds a value with <digits> of numbers after the point.
	 */
	round: function(number, digits) {
		// round number
		var factor = Math.pow(10, digits);
		number = (Math.round(number*factor)) / factor;

		// format number
		try {
			const locale = naviki.CONSTANTS.LOCALE_ALL.replace("_","-");
			const options = { minimumFractionDigits: digits, maximumFractionDigits: digits };
			return Number(number).toLocaleString(locale, options);
		} catch (e) {
			console.log("Can not format number: " + number);
			return number;
		}
	}

}
if (typeof naviki == "undefined") {
	var naviki = {}
}
if (typeof naviki.util == "undefined") {
	naviki.util = {}
}

naviki.util.user = {

	getUserProfile: function() {
		return this.getStorageValueFromJson("_n_u_userprofile");
	},

	setUserProfile: function(userProfile) {
		this.setStorageValueAsJson("_n_u_userprofile", userProfile);
	},

	getInitialWayPrivacyValue: function() {
		const userProfile = this.getUserProfile();
		return userProfile != null ? userProfile.wayPrivacy : 0; // fallback = public
	},
		
	isLoggedIn: function() {
		return this.getUsername() != null && this.getUserId() != null;
	},
	
	getUsername: function() {
		return this.getStorageValue("_n_u_n");
	},

	setUsername: function(username) {
		this.setStorageValue("_n_u_n", username);
	},

	getUserId: function() {
		return this.getStorageValue("_n_u_id");
	},

	setUserId: function(userId) {
		this.setStorageValue("_n_u_id", userId);
	},
	
	isActivated: function() {
		const userState = this.getStorageValue("_n_u_act");
		return userState == null ? false : userState == "y" ? true : false;
	},
	
	setActivated: function(status) {
		// saving of booleans causes problems
		this.setStorageValue("_n_u_act", status ? "y" : "n");
	},
	
	getUserAverageSpeed: function() {
		const avgSpeed = this.getStorageValue("_n_u_asp");
		return avgSpeed != null ? avgSpeed : 15;
	},
	
	setUserAverageSpeed: function(avgSpeed) {
		this.setStorageValue("_n_u_asp", avgSpeed);
	},

	getUserEnergyUnit: function() {
		return this.getStorageValue("_n_u_ueu");
	},

	setUserEnergyUnit: function(energyUnit) {
		this.setStorageValue("_n_u_ueu", energyUnit);
	},

	getUserLengthUnit: function() {
		return this.getStorageValue("_n_u_ulu");
	},

	setUserLengthUnit: function(lengthUnit) {
		this.setStorageValue("_n_u_ulu", lengthUnit);
	},

	getUserWayFilters: function() {
		return this.getStorageValueFromJson("_n_u_way_filters");
	},

	setUserWayFilters: function(wayFilters) {
		this.setStorageValueAsJson("_n_u_way_filters", wayFilters);
	},

	getUserWaySort: function() {
		return this.getStorageValue("_n_u_way_sort");
	},

	setUserWaySort: function(waySort) {
		this.setStorageValue("_n_u_way_sort", waySort);
	},

	removeDeprecatedEntries: function() {
		this.setStorageValue("_n_u_s", null);
	},

	getStorageValue: function(key) {
		if (typeof(localStorage) !== "undefined") {
			const storageValue = window.localStorage.getItem(key);
			if (storageValue != "undefined" && storageValue != null) {
				return storageValue;
			}
		}
		return null;
	},

	getStorageValueFromJson: function(key) {
		const storageValue = this.getStorageValue(key);
		return storageValue == null ? null : JSON.parse(storageValue);
	},

	setStorageValue: function(key, storageValue) {
		if (typeof(localStorage) !== "undefined") {
			if (storageValue == null) {
				window.localStorage.removeItem(key);
			} else {
				window.localStorage.setItem(key, storageValue);
			}
		}
	},

	setStorageValueAsJson: function(key, storageObject) {
		const storageValue = storageObject == null ? null : JSON.stringify(storageObject);
		this.setStorageValue(key, storageValue);
	}
}
if (typeof naviki == "undefined") {
	var naviki = {}
}

if (typeof naviki.util == "undefined") {
	naviki.util = {}
}

naviki.util.pixel = {
	
		// distance from a point to a segment between two points
		pointToSegmentDistance:  function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) {
			return Math.sqrt(this._sqClosestPointOnSegment(p, p1, p2, true));
		},
		
		closestPointOnSegment: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) {
			return this._sqClosestPointOnSegment(p, p1, p2);
		},
		
		// return closest point on segment or distance to that point
		_sqClosestPointOnSegment: function (p, p1, p2, sqDist) {
			var x = p1.x,
				y = p1.y,
				dx = p2.x - x,
				dy = p2.y - y,
				dot = dx * dx + dy * dy,
				t;

			if (dot > 0) {
				t = ((p.x - x) * dx + (p.y - y) * dy) / dot;

				if (t > 1) {
					x = p2.x;
					y = p2.y;
				} else if (t > 0) {
					x += dx * t;
					y += dy * t;
				}
			}

			dx = p.x - x;
			dy = p.y - y;

			return sqDist ? dx * dx + dy * dy : {x:x, y:y};
		},
		
		// square distance (to avoid unnecessary Math.sqrt calls)
		dist: function (p1, p2) {
			var xs = p2.x - p1.x,
				ys = p2.y - p1.y;		
		
			xs *= xs;
			ys *= ys;
		 
			return Math.sqrt( xs + ys );
		},
}
/*
 *  jQuery Boilerplate - v3.3.1
 *  A jump-start for jQuery plugins development.
 *  http://jqueryboilerplate.com
 *
 *  Made by Zeno Rocha
 *  Under MIT License
 */
// the semi-colon before function invocation is a safety net against concatenated
// scripts and/or other plugins which may not be closed properly.
;(function($, window, document, undefined) {

	var pluginName = "History";
	
	function Plugin(settings){
		
		var defaults = {
			popstate: null
		};
		
		if (settings){
			$.extend(this, defaults, settings);
		} else {
			$.extend(this, defaults);
		}
		
		this.enable = false;
		this.init();
	}
	
	Plugin.prototype = {
		
		init : function(){
			
			var _self = this;
			
			/*
	         * Note, this is the only difference when using this library,
	         * because the object window.location cannot be overriden,
	         * so library the returns generated "location" object within
	         * an object window.history, so get it out of "history.location".
	         * For browsers supporting "history.pushState" get generated
	         * object "location" with the usual "window.location".
	         */
	        var location = window.history.location || window.location;
	        
	        $(window).on('popstate', function(e) {
	        	try {
		        	if (typeof _self.popstate == "function") {
	        			_self.popstate(history.state);
	        		}
	        	} catch (e) {
	        		console.log(e);
	        	}
	       });
		},
	
		replaceState: function(state, title, url) {
			try {
				if (typeof history != "undefined") {
					if (state == null) {
						state = this.getState();
					}
					history.replaceState(state, title, url);
				}
			} catch (e) {
				console.log(e);
			}
		},
		
		pushState: function(state, title, url) {
			try {
				if (typeof history != "undefined") {
					history.pushState(state, title, url);
				}
			} catch (e) {
				console.log(e);
			}
		},
		
		updateURL: function(url) {
			try {
				if (typeof history != "undefined") {
					history.replaceState(this.getState(), "", url);
				}
			} catch (e) {
				console.log(e);
			}
		},
		
		getState: function() {
			try {
				if(typeof history != "undefined") {
					return history.state;
				}
			} catch (e) {
				console.log(e);
			}
			return null;
			
		},
		
		back: function() {
			try {
				if(typeof history != "undefined") {
					history.back();
				}
			} catch (e) {
				console.log(e);
			}
		}
	}
		
	$.fn[pluginName] = function(options) {
		return new Plugin(options);
	}

})(jQuery, window, document);
/**
 * Call function if user clicked on naviki logo.
 * Changes the window location to the start of the landingpage.
 * 
 */
;(function($) {
	$.fn.navikiLogo = function(landingpageTitle) {
		var hrefParts = window.location.href.split('/');
		if (hrefParts[3].length == 2) {
			// we have a language parameter
			window.location.href = hrefParts[0]+"//"+hrefParts[2]+"/"+hrefParts[3]+"/"+landingpageTitle+"/";
		} else {
			window.location.href = hrefParts[0]+"//"+hrefParts[2]+"/"+landingpageTitle+"/";
		}
	};
})( jQuery);
/*
 *  jQuery Boilerplate - v3.3.1
 *  A jump-start for jQuery plugins development.
 *  http://jqueryboilerplate.com
 *
 *  Made by Zeno Rocha
 *  Under MIT License
 */
// the semi-colon before function invocation is a safety net against concatenated
// scripts and/or other plugins which may not be closed properly.
;(function ($, window, document, undefined) {

    var pluginName = "navikiUiDialog";
    var defaults = {
        id: "naviki-ui-dialog",
        close: true, // should show close-icon on top right
        content: "",
        footer: "",
        buttons: null,
        className: null,
        closeCallback: null,
        openCallback: null
    };

    function Plugin(options) {
        this.options = $.extend({}, defaults, options);
        this._defaults = defaults;
        this._name = pluginName;

        this.element = null;
        this.init();
    }

    Plugin.prototype = {

        init: function () {
            if ($("#" + this.options.id).length == 0) {
                this.element = $("<div/>", {id: this.options.id, "class": "naviki-ui-dialog-wrapper"});
                $('body').append(this.element);

                var dialog = $("<div/>", {"class": "naviki-ui-dialog"});
                this.element.append(dialog);

                var container = $("<div/>", {"class": "dialog-container"});
                dialog.append(container);

                container.append($("<div/>", {"class": "header"}));
                container.append($("<div/>", {"class": "content"}));
                container.append($("<div/>", {"class": "buttons"}));
                container.append($("<div/>", {"class": "footer"}));

                $(document).keyup(function(e) {
                    if (e.keyCode == 27 && this.isShown()) { // escape button
                        this.close();
                    }
                }.bind(this));

                if (this.options.className != null) {
                    this.element.addClass(this.options.className);
                }
            } else {
                this.element = $("#" + this.options.id);
            }

            this.element.find('.header').first().empty();
            this.element.find('.buttons').first().empty();
            this.element.find('.content').first().html(this.options.content);
            this.element.find('.footer').first().html(this.options.footer);

            if (this.options.footer.length == 0) {
                this.element.find('.footer').first().css("display", "none");
            } else {
                this.element.find('.footer').first().css("display", "");
            }

            if (this.options.close) {
                this.element.find('.header').first().append($("<span/>", {"class": "icon-close"}));
                this.element.find('.icon-close').first().click(this.close.bind(this));
            }

            if (this.options.buttons != null && $.isArray(this.options.buttons) && this.options.buttons.length > 0) {

                for (var i = 0; i < this.options.buttons.length; i++) {
                    (function (button) {
                        const btn = $("<button/>", {type: "button", "class": "btn btn-default"});
                        btn.text(button.text);
                        btn.click(button.callback);
                        this.element.find('.buttons').first().append(btn);
                    }).bind(this)(this.options.buttons[i]);
                }
            }

            $(window).resize(this.resize.bind(this));
        },

        open: function () {
            if (this.isShown()) {
                return;
            }

            this.element.addClass('visible');
            this.resize();
            if (typeof this.options.openCallback == "function") {
                this.options.openCallback();
            }
        },

        close: function () {
            if (!this.isShown()) {
                return;
            }

            this.element.removeClass('visible');
            if (typeof this.options.closeCallback == "function") {
                this.options.closeCallback();
            }
        },

        isShown: function () {
            return this.element.hasClass('visible');
        },

        resize: function () {
            if (!this.isShown()) {
                return;
            }

            const content = this.element.find('.content').first();
            content.css("height", "");

            const height = content.first().outerHeight();
            const windowHeight = $(window).outerHeight() * 0.9;
            if (height > windowHeight) {
                content.css("height", windowHeight);
            }
        }

    };

    $.fn[pluginName] = function (options) {
        return new Plugin(options);
    }

})(jQuery, window, document);
/*
 *  jQuery Boilerplate - v3.3.1
 *  A jump-start for jQuery plugins development.
 *  http://jqueryboilerplate.com
 *
 *  Made by Zeno Rocha
 *  Under MIT License
 */
// the semi-colon before function invocation is a safety net against concatenated
// scripts and/or other plugins which may not be closed properly.
;(function ( $, window, document, undefined ) {

		// undefined is used here as the undefined global variable in ECMAScript 3 is
		// mutable (ie. it can be changed by someone else). undefined isn't really being
		// passed in so we can ensure the value of it is truly undefined. In ES5, undefined
		// can no longer be modified.

		// window and document are passed through as local variable rather than global
		// as this (slightly) quickens the resolution process and can be more efficiently
		// minified (especially when both are regularly referenced in your plugin).

		// Create the defaults once
		var pluginName = "navikiUiLoading";
		var defaults = {
				id : null,
				zindex: null
		};

		// The actual plugin constructor
		function Plugin ( element, options ) {
				
				this.calcualte = function(){				
					$("#loader-overlay-" + this.options.id).css({
						opacity : 0.15,
						top : $(this.element).position().top,
						left: $(this.element).position().left,
						width : $(this.element).outerWidth(),
						height: $(this.element).outerHeight()
					});
					$("#loader-img-" + this.options.id).css({
						top : ($(this.element).height() / 2 - 9),
						left: ($(this.element).width() / 2 - 9)
					});
				};
				
				var uniqID = {
					counter:0,
					get:function(prefix) {
						if(!prefix) {
							prefix = "uniqid";
						}
						var id =  prefix+""+uniqID.counter++;
						if (jQuery("#"+id).length == 0) {
							return id;
						} else {
							return uniqID.get()
						}
					}
				}

				this.element = element;
				// jQuery has an extend method which merges the contents of two or
				// more objects, storing the result in the first object. The first object
				// is generally empty as we don't want to alter the default options for
				// future instances of the plugin
				this.options = $.extend( {}, defaults, options );
				this.options.id = uniqID.get();
				this._defaults = defaults;
				this._name = pluginName;
				this.init();
		}

		Plugin.prototype = {
				init: function () {
					// Place initialization logic here
					// You already have access to the DOM element and
					// the options via the instance, e.g. this.element
					// and this.options
					// you can add more functions like the one below and
					// call them like so: this.yourOtherFunction(this.element, this.options).		
					//this.options.id = $(this.element).uniqueId().attr( "id" );
					var _self = this;
					var node = "<div id='loader-overlay-" + this.options.id + "' class='naviki-ui-loader-overlay'";
					if(this.options.zindex != null) {
						node += " style='z-index:" + this.options.zindex + ";'";
					}
					node += ">" +
					"<img class='naviki-ui-loader-img' src='typo3conf/ext/naviki/Resources/Public/Img/ajax-loader-2.gif' id='loader-img-" + this.options.id + "' />" +
					"</div>";		
					$(this.element).append(node);
					$(window).resize(function() {
						_self.calcualte();
					});
				},
				hide : function(args) {
					$("#loader-overlay-" + this.options.id).fadeOut(0);
				},
				show : function(args) {
					this.calcualte();
					$("#loader-overlay-" + this.options.id).fadeIn(0);
				}
		};

		// A really lightweight plugin wrapper around the constructor, 
		// preventing against multiple instantiations and allowing any
		// public function (ie. a function whose name doesn't start
		// with an underscore) to be called via the jQuery plugin,
		// e.g. $(element).defaultPluginName('functionName', arg1, arg2)
		$.fn[ pluginName ] = function ( options ) {
			var args = arguments;
			if (options === undefined || typeof options === 'object') {
				return this.each(function () {
		            if (!$.data(this, 'plugin_' + pluginName)) {
		                $.data(this, 'plugin_' + pluginName, new Plugin( this, options ));
		            }
		        });
		    } else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') {
		        return this.each(function () {
		            var instance = $.data(this, 'plugin_' + pluginName);
		            if (instance instanceof Plugin && typeof instance[options] === 'function') {
		                instance[options].apply( instance, Array.prototype.slice.call( args, 1 ) );
		            }
		        });
		    }
		};

})( jQuery, window, document );
/* jQuery Boilerplate - v3.3.1 */
;(function ( $, window, document, undefined ) {

		var pluginName = "navikiUiUserActivationHint";
		var defaults = {
				oauth2MicroServiceHostname: ""
		};

		function Plugin (options) {
				this.options = $.extend( {}, defaults, options );
				this._defaults = defaults;
				this._name = pluginName;
				
				this.element = null;
				this.init();
		}

		Plugin.prototype = {
				init: function () {
					if (naviki.util.user.isLoggedIn() && !naviki.util.user.isActivated()) {
						this._requestActivationStatus();
					}
				},
				
				_showUserActivationHintBar: function(email) {
					var _this = this;

					var hintText = naviki.Lang.getLL('base.deactivatedAccountHint');
					hintText = hintText.replace("{0}", email);

					var linkUrl = this.options.oauth2MicroServiceHostname + "/reactivateUser";
					if (naviki.util.user.getUsername() != null)
						linkUrl += "?emailPlaceholder=" + naviki.util.user.getUsername();

					var content = "<div class='cookie-choice-info warning'><div>";
					content += "<span>" + hintText + "</span>";
					content += "<a href='" + linkUrl + "' id='user-activation-resend-button'>" + naviki.Lang.getLL('base.deactivatedAccountResendButton') + "</a>";
					content += "<a href='" + naviki.CONSTANTS.LANGUAGE + "/naviki/intern/my-naviki-profile-and-settings/'>" + naviki.Lang.getLL('base.deactivatedAccountChangeEmailButton') + "</a>";
					content += "</div></div>";
						
					if ($('#fixedHeader').length > 0) {
						$('#fixedHeader').prepend(content);
					} else {
						$('body').prepend(content);
					}

					$("#user-activation-resend-button").click(function(event) {
						event.preventDefault();
						var url = $(this).attr("href");
						_this._openDialog(url);	
					});	
				},
		 		
		 		_requestActivationStatus: function() {
					var _this = this;
					$.ajax({
						url: this.options.oauth2MicroServiceHostname + "/api/user/activatedAccount",
						type: "POST",
						cache: false,
						dataType : "json",
						contentType: 'application/json; charset=utf-8',
					    processData: false,
					    beforeSend: function(xhr) {
							if (naviki.util.api.getAccessToken() != null) {
								xhr.setRequestHeader('Authorization', 'Bearer ' + naviki.util.api.getAccessToken());
							}
					    },
						success: function(data) {
							if (typeof data.valid !== 'undefined') {
								if (data.valid) {
									naviki.util.user.setActivated(true);
								} else {
									_this._showUserActivationHintBar(data.email);
								}
							}
						},
						error: function (request, status, error) {
							console.log("Error in jquery.naviki.ui.useractivationhint.js: " + error);
		                }
					});
				},
				
				_openDialog: function(url) {	
					this.dialog = $.colorbox({
						width: 320, 
						height: 480, 
						scrolling: false,
						iframe: true, 
						href: url
					});
				}
		};

		$.fn[pluginName] = function(options) {
			return new Plugin(options);
		}
})( jQuery, window, document );
/*
 *  jQuery Boilerplate - v3.3.1
 *  A jump-start for jQuery plugins development.
 *  http://jqueryboilerplate.com
 *
 *  Made by Zeno Rocha
 *  Under MIT License
 */
// the semi-colon before function invocation is a safety net against concatenated
// scripts and/or other plugins which may not be closed properly.
;
(function($, window, document, undefined) {

	// undefined is used here as the undefined global variable in ECMAScript 3 is
	// mutable (ie. it can be changed by someone else). undefined isn't really being
	// passed in so we can ensure the value of it is truly undefined. In ES5, undefined
	// can no longer be modified.

	// window and document are passed through as local variable rather than global
	// as this (slightly) quickens the resolution process and can be more efficiently
	// minified (especially when both are regularly referenced in your plugin).

	// Create the defaults once
	var pluginName = "navikiUserAuth";
	var defaults = {};

	// The actual plugin constructor
	function Plugin(element, options) {

		this.element = element;
		// jQuery has an extend method which merges the contents of two or
		// more objects, storing the result in the first object. The first object
		// is generally empty as we don't want to alter the default options for
		// future instances of the plugin
		this.options = $.extend({}, defaults, options);
		this._defaults = defaults;
		this._name = pluginName;
		
		this.history = new $.fn.History();
		this.logoutDialog = null;
		this.loginDialog = null;
		this.loginDialogWidth = 320;
		this.loginDialogHeight = 480;
		
		this.init();
	}

	Plugin.prototype = {

		init : function() {
			// Place initialization logic here
			// You already have access to the DOM element and
			// the options via the instance, e.g. this.element
			// and this.options
			// you can add more functions like the one below and
			// call them like so: this.yourOtherFunction(this.element, this.options).		
			var _this = this;	
			
			// remove unused item
			if(typeof(localStorage) !== "undefined") {	
				window.localStorage.removeItem("_n_um");
				window.localStorage.removeItem("_n_un");
				window.localStorage.removeItem("_n_us");
			}
			
			if (window.location != window.parent.location) { 
				// THE PAGE IS OPENED INSIDE A IFRAME
				// event listener for cross origin communication,
				window.addEventListener("message", function(event) {
					
					// Do we trust the sender of this message?
				    var domain = naviki.util.url.extractDomain(event.origin);
				    if (domain !== "localhost:18080" &&
				        domain !== "naviki.org") {
				        return;
				    }
					
					if (event.data.get == "search") {
						event.source.postMessage({search:window.location.search},  event.origin);
					}
				}, false);	
			} else {
				// THE PAGE IS OPENED INSIDE A BROWSER TAB
				// event listener for cross origin communication,
				window.addEventListener("message", function(event) {
					
					// Do we trust the sender of this message?
				    var domain = naviki.util.url.extractDomain(event.origin);
				    if (domain !== "localhost:18080" &&
				        domain !== "naviki.org") {
				        return;
				    }	
					
					if (_this.loginDialog != null) {
						if (event.data.height) {
							//enough height for g-recaptcha
							const newHeight = event.data.height < 522 ? 522 : event.data.height;
							_this.loginDialog.colorbox.resize({
								innerWidth: _this.loginDialogWidth,
								innerHeight: newHeight
							});
						}
						if (event.data.search) {
							var query = naviki.util.url.decodeParams(decodeURI(event.data.search.substring(1)));
							_this.initLogin(query);
							setTimeout(function(){
								_this.loginDialog.colorbox.close();
								_this.loginDialog.colorbox.remove();
							}, 3000);
						}
					}
				}, false);			

				// open the login/register page inside a iframe
				$("#header-register-container a").first().click(function(event) {
					event.preventDefault();

					// NAV-7526 - on mobile devices redirect to map page
					if (_this.isMobileView()) {
						// first page in menu is usually the map which contains the mobile container ("open naviki app")
						_this.triggerFirstMainMenuSubPage();
						return;
					}

					const url = $(this).attr("href");
					_this.openLoginDialog(url);	
				});

				$("#header-login-container a").first().click(function(event) {
					event.preventDefault();

					// NAV-7526 - on mobile devices redirect to map page
					if (_this.isMobileView()) {
						// first page in menu is usually the map which contains the mobile container ("open naviki app")
						_this.triggerFirstMainMenuSubPage();
						return;
					}

					const url = $(this).attr("href");
					_this.openLoginDialog(url);
				});	

				// catch url query parameters as object
				var query = naviki.util.url.decodeParams(decodeURI(window.location.search.substring(1)));	
				this.initLogin(query);
			}
		},
		
		initLogin: function(query) {
			if (query.login != undefined) {
				// catch typo3 sociallogin
				this.login(null);
				// remove GET params
				var url = naviki.util.url.removeParam("login");
				this.history.replaceState(null,null,url);
			} else if (query.code) {
				// catch java oauth2
				this.login(query.code);
				// remove GET params
				var url = naviki.util.url.removeParams(new Array("code","state"));
				this.history.replaceState(null,null,url);
			} else if(naviki.util.user.isLoggedIn() && naviki.util.user.getUsername() != null) {
				this.showMenuBtn();
			} else {
				this.showLoginBtn();
			}
		},
		
		login: function(code) {
			var params = "?request[format]=json&request[controller]=FeUser&request[action]=feSession";
			params += "&request[arguments][code]=" + code;

			$.ajax({
				url: naviki.util.api.getAjaxDispatcherBasePath() + encodeURI(params),
				type: "GET",
				cache: false,
				dataType : "json",
				contentType: 'application/json; charset=utf-8',
			    processData: false,
				success : function(data) {
					if (data.status) {	
						naviki.util.api.setAccessToken(data.accessToken);
						naviki.util.api.setRefreshToken(data.refreshToken);
						naviki.util.user.setUsername(data.username);
						naviki.util.user.setUserId(data.feUserId);
						this.showMenuBtn();

						naviki.util.api.loadUserProfile();
					} else {
						_this.showLoginBtn();
					}
					$(window).trigger("naviki_loginSuccess");

					// when user has successfully changed password, it comes to redirect page and this point
					// user is logged in now and tokens are set
					// in this case, wer must manually redirect to naviki start page
					if (window.location.pathname.indexOf("/naviki/single-pages/loading/mobile.html") != -1) {
						document.location.replace(location.origin);
					}
				}.bind(this),
				beforeSend: this.clear.bind(this),
				complete: function() {},
				error: this.showLoginBtn.bind(this)
			});
		},

		logout: function(targetUrl) {
			this.logoutDialog = new $.fn.navikiUiDialog({
				id: "naviki-ui-logout-dialog",
				content: naviki.Lang.getLL('pi_userprofile.logoutDialog'),
				buttons:[{
					text: naviki.Lang.getLL('base.yes'),
					callback: function() {
						this.logoutUser(targetUrl);
					}.bind(this)
				},{
					text: naviki.Lang.getLL('base.no'),
					callback: this.closeLogoutDialog.bind(this)
				}]
			});
			this.logoutDialog.open();
		},

		closeLogoutDialog: function() {
			if (this.logoutDialog != null) {
				this.logoutDialog.close();
			}
		},
		
		showLoginBtn: function() {
			this.clear();
			$("#header-dropdown-container").fadeOut("slow",function(){
				$("#header-login-container").fadeIn("slow");
				$("#header-register-container").fadeIn("slow");
			});
		},
		
		showMenuBtn: function(loadItems) {
			$("#header-register-container").fadeOut("slow");
			
			$("#header-login-container").fadeOut("slow",function() {
				$("#header-username").text(naviki.util.user.getUsername());
				$("#header-dropdown-container").fadeIn( "slow" );
			});

			$("#header-dropdown-btn").one("click",this.loadMenu.bind(this));
		},
		
		loadMenu: function() {
			const params = "?request[format]=json&request[controller]=FeUser&request[action]=feMenu";
			$.ajax({
				url: naviki.util.api.getAjaxDispatcherBasePath() + encodeURI(params),
				type: "GET",
				cache: false,
				dataType : "json",
				contentType: 'application/json; charset=utf-8',
			    processData: false,
				success : function(data) {
					if (data.status) {
						$("#header-dropdown-menu").html(data.menuItems);
						this.initLogoutButton();
					} else {
						this.showLoginBtn();
					}
				}.bind(this),
				beforeSend: function() {
					$("#header-caret").css("visibility", "hidden");
					$("#header-loading").addClass("spinning-wheel");
					$("#header-dropdown-menu").css("border-width","0");
				},
				complete: function() {
					$("#header-caret").css("visibility", "");
					$("#header-loading").removeClass("spinning-wheel");
					$("#header-dropdown-menu").css("border-width","1px");
				},
				error: function(jqXHR, textStatus, errorThrown) {
					// do not logout if server was too busy
					if (jqXHR != null && jqXHR.status != null && jqXHR.status == 504) {
						return;
					}
					this.showLoginBtn();
				}.bind(this)
			});
		},

		initLogoutButton: function() {
			const logoutButton = $("#header-dropdown-container ul li:last-child")
			logoutButton.on("click", function(event) {
				event.preventDefault();
				const targetUrl = logoutButton.find("a:first-child").attr("href");
				this.logout(targetUrl);
			}.bind(this));
		},
		
		clear: function() {
			naviki.util.api.logoutFeUser();
		},

		logoutUser: function(targetUrl) {
			const params = "?request[format]=json&request[controller]=FeUser&request[action]=logout";
			naviki.util.api.request({
				url: naviki.util.api.getAjaxDispatcherBasePath() + encodeURI(params),
				type: "GET",
				cache: false,
				dataType : "json",
				contentType: 'application/json; charset=utf-8',
				processData: false,
				success : function(data) { console.log(data); },
				beforeSend: function() {
					$("#naviki-ui-logout-dialog").navikiUiLoading();
					$("#naviki-ui-logout-dialog").navikiUiLoading("show");
				},
				complete: function() {
					$("#naviki-ui-logout-dialog").navikiUiLoading("hide");
					this.closeLogoutDialog();

					// always continue logout
					this.clear();
					location.href = targetUrl
				}.bind(this),
				error: function(error) { console.log(error); }
			});
		},
		
		openLoginDialog: function(url) {	
			this.loginDialog = $.colorbox({
				width: this.loginDialogWidth,
				height: this.loginDialogHeight,
				scrolling: false,
				iframe: true, 
				href: url,
				onComplete : function() {
					// avoid opaque title bar
					$('#colorbox').css("background", "#ffffff");
					
					var frame = $('#colorbox iframe').get(0);
					$(frame).load(function(e) {
						frame.contentWindow.postMessage({get:"height"}, "*");
						frame.contentWindow.postMessage({get:"search"}, "*");
					});
				} 
			});
		},

		isMobileView: function() {
			// because of css rules like @media (max-width: 767px)
			return $(window).width() <= 767;
		},

		triggerFirstMainMenuSubPage: function() {
			// native click() on a-tag
			$('div#navbar div.main-nav:first ul.nav:first li:first a:first')[0].click();
		}
	};
	
	// A really lightweight plugin wrapper around the constructor, 
	// preventing against multiple instantiations and allowing any
	// public function (ie. a function whose name doesn't start
	// with an underscore) to be called via the jQuery plugin,
	// e.g. $(element).defaultPluginName('functionName', arg1, arg2)
	$.fn[pluginName] = function(options) {
		var args = arguments;
		if (options === undefined || typeof options === 'object') {
			return this.each(function() {
				if (!$.data(this, 'plugin_' + pluginName)) {
					$.data(this, 'plugin_' + pluginName, new Plugin(this, options));
				}
			});
		} else if (typeof options === 'string' && options[0] !== '_'
				&& options !== 'init') {
			return this.each(function() {
				var instance = $.data(this, 'plugin_' + pluginName);
				if (instance instanceof Plugin && typeof instance[options] === 'function') {
					instance[options].apply(instance, Array.prototype.slice .call(args, 1));
				}
			});
		}
	};

})(jQuery, window, document);

/*!
	Colorbox 1.6.4
	license: MIT
	http://www.jacklmoore.com/colorbox
*/
(function(t,e,i){function n(i,n,o){var r=e.createElement(i);return n&&(r.id=Z+n),o&&(r.style.cssText=o),t(r)}function o(){return i.innerHeight?i.innerHeight:t(i).height()}function r(e,i){i!==Object(i)&&(i={}),this.cache={},this.el=e,this.value=function(e){var n;return void 0===this.cache[e]&&(n=t(this.el).attr("data-cbox-"+e),void 0!==n?this.cache[e]=n:void 0!==i[e]?this.cache[e]=i[e]:void 0!==X[e]&&(this.cache[e]=X[e])),this.cache[e]},this.get=function(e){var i=this.value(e);return t.isFunction(i)?i.call(this.el,this):i}}function h(t){var e=W.length,i=(A+t)%e;return 0>i?e+i:i}function a(t,e){return Math.round((/%/.test(t)?("x"===e?E.width():o())/100:1)*parseInt(t,10))}function s(t,e){return t.get("photo")||t.get("photoRegex").test(e)}function l(t,e){return t.get("retinaUrl")&&i.devicePixelRatio>1?e.replace(t.get("photoRegex"),t.get("retinaSuffix")):e}function d(t){"contains"in x[0]&&!x[0].contains(t.target)&&t.target!==v[0]&&(t.stopPropagation(),x.focus())}function c(t){c.str!==t&&(x.add(v).removeClass(c.str).addClass(t),c.str=t)}function g(e){A=0,e&&e!==!1&&"nofollow"!==e?(W=t("."+te).filter(function(){var i=t.data(this,Y),n=new r(this,i);return n.get("rel")===e}),A=W.index(_.el),-1===A&&(W=W.add(_.el),A=W.length-1)):W=t(_.el)}function u(i){t(e).trigger(i),ae.triggerHandler(i)}function f(i){var o;if(!G){if(o=t(i).data(Y),_=new r(i,o),g(_.get("rel")),!U){U=$=!0,c(_.get("className")),x.css({visibility:"hidden",display:"block",opacity:""}),I=n(se,"LoadedContent","width:0; height:0; overflow:hidden; visibility:hidden"),b.css({width:"",height:""}).append(I),j=T.height()+k.height()+b.outerHeight(!0)-b.height(),D=C.width()+H.width()+b.outerWidth(!0)-b.width(),N=I.outerHeight(!0),z=I.outerWidth(!0);var h=a(_.get("initialWidth"),"x"),s=a(_.get("initialHeight"),"y"),l=_.get("maxWidth"),f=_.get("maxHeight");_.w=Math.max((l!==!1?Math.min(h,a(l,"x")):h)-z-D,0),_.h=Math.max((f!==!1?Math.min(s,a(f,"y")):s)-N-j,0),I.css({width:"",height:_.h}),J.position(),u(ee),_.get("onOpen"),O.add(F).hide(),x.focus(),_.get("trapFocus")&&e.addEventListener&&(e.addEventListener("focus",d,!0),ae.one(re,function(){e.removeEventListener("focus",d,!0)})),_.get("returnFocus")&&ae.one(re,function(){t(_.el).focus()})}var p=parseFloat(_.get("opacity"));v.css({opacity:p===p?p:"",cursor:_.get("overlayClose")?"pointer":"",visibility:"visible"}).show(),_.get("closeButton")?B.html(_.get("close")).appendTo(b):B.appendTo("<div/>"),w()}}function p(){x||(V=!1,E=t(i),x=n(se).attr({id:Y,"class":t.support.opacity===!1?Z+"IE":"",role:"dialog",tabindex:"-1"}).hide(),v=n(se,"Overlay").hide(),L=t([n(se,"LoadingOverlay")[0],n(se,"LoadingGraphic")[0]]),y=n(se,"Wrapper"),b=n(se,"Content").append(F=n(se,"Title"),R=n(se,"Current"),P=t('<button type="button"/>').attr({id:Z+"Previous"}),K=t('<button type="button"/>').attr({id:Z+"Next"}),S=t('<button type="button"/>').attr({id:Z+"Slideshow"}),L),B=t('<button type="button"/>').attr({id:Z+"Close"}),y.append(n(se).append(n(se,"TopLeft"),T=n(se,"TopCenter"),n(se,"TopRight")),n(se,!1,"clear:left").append(C=n(se,"MiddleLeft"),b,H=n(se,"MiddleRight")),n(se,!1,"clear:left").append(n(se,"BottomLeft"),k=n(se,"BottomCenter"),n(se,"BottomRight"))).find("div div").css({"float":"left"}),M=n(se,!1,"position:absolute; width:9999px; visibility:hidden; display:none; max-width:none;"),O=K.add(P).add(R).add(S)),e.body&&!x.parent().length&&t(e.body).append(v,x.append(y,M))}function m(){function i(t){t.which>1||t.shiftKey||t.altKey||t.metaKey||t.ctrlKey||(t.preventDefault(),f(this))}return x?(V||(V=!0,K.click(function(){J.next()}),P.click(function(){J.prev()}),B.click(function(){J.close()}),v.click(function(){_.get("overlayClose")&&J.close()}),t(e).bind("keydown."+Z,function(t){var e=t.keyCode;U&&_.get("escKey")&&27===e&&(t.preventDefault(),J.close()),U&&_.get("arrowKey")&&W[1]&&!t.altKey&&(37===e?(t.preventDefault(),P.click()):39===e&&(t.preventDefault(),K.click()))}),t.isFunction(t.fn.on)?t(e).on("click."+Z,"."+te,i):t("."+te).live("click."+Z,i)),!0):!1}function w(){var e,o,r,h=J.prep,d=++le;if($=!0,q=!1,u(he),u(ie),_.get("onLoad"),_.h=_.get("height")?a(_.get("height"),"y")-N-j:_.get("innerHeight")&&a(_.get("innerHeight"),"y"),_.w=_.get("width")?a(_.get("width"),"x")-z-D:_.get("innerWidth")&&a(_.get("innerWidth"),"x"),_.mw=_.w,_.mh=_.h,_.get("maxWidth")&&(_.mw=a(_.get("maxWidth"),"x")-z-D,_.mw=_.w&&_.w<_.mw?_.w:_.mw),_.get("maxHeight")&&(_.mh=a(_.get("maxHeight"),"y")-N-j,_.mh=_.h&&_.h<_.mh?_.h:_.mh),e=_.get("href"),Q=setTimeout(function(){L.show()},100),_.get("inline")){var c=t(e).eq(0);r=t("<div>").hide().insertBefore(c),ae.one(he,function(){r.replaceWith(c)}),h(c)}else _.get("iframe")?h(" "):_.get("html")?h(_.get("html")):s(_,e)?(e=l(_,e),q=_.get("createImg"),t(q).addClass(Z+"Photo").bind("error."+Z,function(){h(n(se,"Error").html(_.get("imgError")))}).one("load",function(){d===le&&setTimeout(function(){var e;_.get("retinaImage")&&i.devicePixelRatio>1&&(q.height=q.height/i.devicePixelRatio,q.width=q.width/i.devicePixelRatio),_.get("scalePhotos")&&(o=function(){q.height-=q.height*e,q.width-=q.width*e},_.mw&&q.width>_.mw&&(e=(q.width-_.mw)/q.width,o()),_.mh&&q.height>_.mh&&(e=(q.height-_.mh)/q.height,o())),_.h&&(q.style.marginTop=Math.max(_.mh-q.height,0)/2+"px"),W[1]&&(_.get("loop")||W[A+1])&&(q.style.cursor="pointer",t(q).bind("click."+Z,function(){J.next()})),q.style.width=q.width+"px",q.style.height=q.height+"px",h(q)},1)}),q.src=e):e&&M.load(e,_.get("data"),function(e,i){d===le&&h("error"===i?n(se,"Error").html(_.get("xhrError")):t(this).contents())})}var v,x,y,b,T,C,H,k,W,E,I,M,L,F,R,S,K,P,B,O,_,j,D,N,z,A,q,U,$,G,Q,J,V,X={html:!1,photo:!1,iframe:!1,inline:!1,transition:"elastic",speed:300,fadeOut:300,width:!1,initialWidth:"600",innerWidth:!1,maxWidth:!1,height:!1,initialHeight:"450",innerHeight:!1,maxHeight:!1,scalePhotos:!0,scrolling:!0,opacity:.9,preloading:!0,className:!1,overlayClose:!0,escKey:!0,arrowKey:!0,top:!1,bottom:!1,left:!1,right:!1,fixed:!1,data:void 0,closeButton:!0,fastIframe:!0,open:!1,reposition:!0,loop:!0,slideshow:!1,slideshowAuto:!0,slideshowSpeed:2500,slideshowStart:"start slideshow",slideshowStop:"stop slideshow",photoRegex:/\.(gif|png|jp(e|g|eg)|bmp|ico|webp|jxr|svg)((#|\?).*)?$/i,retinaImage:!1,retinaUrl:!1,retinaSuffix:"@2x.$1",current:"image {current} of {total}",previous:"previous",next:"next",close:"close",xhrError:"This content failed to load.",imgError:"This image failed to load.",returnFocus:!0,trapFocus:!0,onOpen:!1,onLoad:!1,onComplete:!1,onCleanup:!1,onClosed:!1,rel:function(){return this.rel},href:function(){return t(this).attr("href")},title:function(){return this.title},createImg:function(){var e=new Image,i=t(this).data("cbox-img-attrs");return"object"==typeof i&&t.each(i,function(t,i){e[t]=i}),e},createIframe:function(){var i=e.createElement("iframe"),n=t(this).data("cbox-iframe-attrs");return"object"==typeof n&&t.each(n,function(t,e){i[t]=e}),"frameBorder"in i&&(i.frameBorder=0),"allowTransparency"in i&&(i.allowTransparency="true"),i.name=(new Date).getTime(),i.allowFullscreen=!0,i}},Y="colorbox",Z="cbox",te=Z+"Element",ee=Z+"_open",ie=Z+"_load",ne=Z+"_complete",oe=Z+"_cleanup",re=Z+"_closed",he=Z+"_purge",ae=t("<a/>"),se="div",le=0,de={},ce=function(){function t(){clearTimeout(h)}function e(){(_.get("loop")||W[A+1])&&(t(),h=setTimeout(J.next,_.get("slideshowSpeed")))}function i(){S.html(_.get("slideshowStop")).unbind(s).one(s,n),ae.bind(ne,e).bind(ie,t),x.removeClass(a+"off").addClass(a+"on")}function n(){t(),ae.unbind(ne,e).unbind(ie,t),S.html(_.get("slideshowStart")).unbind(s).one(s,function(){J.next(),i()}),x.removeClass(a+"on").addClass(a+"off")}function o(){r=!1,S.hide(),t(),ae.unbind(ne,e).unbind(ie,t),x.removeClass(a+"off "+a+"on")}var r,h,a=Z+"Slideshow_",s="click."+Z;return function(){r?_.get("slideshow")||(ae.unbind(oe,o),o()):_.get("slideshow")&&W[1]&&(r=!0,ae.one(oe,o),_.get("slideshowAuto")?i():n(),S.show())}}();t[Y]||(t(p),J=t.fn[Y]=t[Y]=function(e,i){var n,o=this;return e=e||{},t.isFunction(o)&&(o=t("<a/>"),e.open=!0),o[0]?(p(),m()&&(i&&(e.onComplete=i),o.each(function(){var i=t.data(this,Y)||{};t.data(this,Y,t.extend(i,e))}).addClass(te),n=new r(o[0],e),n.get("open")&&f(o[0])),o):o},J.position=function(e,i){function n(){T[0].style.width=k[0].style.width=b[0].style.width=parseInt(x[0].style.width,10)-D+"px",b[0].style.height=C[0].style.height=H[0].style.height=parseInt(x[0].style.height,10)-j+"px"}var r,h,s,l=0,d=0,c=x.offset();if(E.unbind("resize."+Z),x.css({top:-9e4,left:-9e4}),h=E.scrollTop(),s=E.scrollLeft(),_.get("fixed")?(c.top-=h,c.left-=s,x.css({position:"fixed"})):(l=h,d=s,x.css({position:"absolute"})),d+=_.get("right")!==!1?Math.max(E.width()-_.w-z-D-a(_.get("right"),"x"),0):_.get("left")!==!1?a(_.get("left"),"x"):Math.round(Math.max(E.width()-_.w-z-D,0)/2),l+=_.get("bottom")!==!1?Math.max(o()-_.h-N-j-a(_.get("bottom"),"y"),0):_.get("top")!==!1?a(_.get("top"),"y"):Math.round(Math.max(o()-_.h-N-j,0)/2),x.css({top:c.top,left:c.left,visibility:"visible"}),y[0].style.width=y[0].style.height="9999px",r={width:_.w+z+D,height:_.h+N+j,top:l,left:d},e){var g=0;t.each(r,function(t){return r[t]!==de[t]?(g=e,void 0):void 0}),e=g}de=r,e||x.css(r),x.dequeue().animate(r,{duration:e||0,complete:function(){n(),$=!1,y[0].style.width=_.w+z+D+"px",y[0].style.height=_.h+N+j+"px",_.get("reposition")&&setTimeout(function(){E.bind("resize."+Z,J.position)},1),t.isFunction(i)&&i()},step:n})},J.resize=function(t){var e;U&&(t=t||{},t.width&&(_.w=a(t.width,"x")-z-D),t.innerWidth&&(_.w=a(t.innerWidth,"x")),I.css({width:_.w}),t.height&&(_.h=a(t.height,"y")-N-j),t.innerHeight&&(_.h=a(t.innerHeight,"y")),t.innerHeight||t.height||(e=I.scrollTop(),I.css({height:"auto"}),_.h=I.height()),I.css({height:_.h}),e&&I.scrollTop(e),J.position("none"===_.get("transition")?0:_.get("speed")))},J.prep=function(i){function o(){return _.w=_.w||I.width(),_.w=_.mw&&_.mw<_.w?_.mw:_.w,_.w}function a(){return _.h=_.h||I.height(),_.h=_.mh&&_.mh<_.h?_.mh:_.h,_.h}if(U){var d,g="none"===_.get("transition")?0:_.get("speed");I.remove(),I=n(se,"LoadedContent").append(i),I.hide().appendTo(M.show()).css({width:o(),overflow:_.get("scrolling")?"auto":"hidden"}).css({height:a()}).prependTo(b),M.hide(),t(q).css({"float":"none"}),c(_.get("className")),d=function(){function i(){t.support.opacity===!1&&x[0].style.removeAttribute("filter")}var n,o,a=W.length;U&&(o=function(){clearTimeout(Q),L.hide(),u(ne),_.get("onComplete")},F.html(_.get("title")).show(),I.show(),a>1?("string"==typeof _.get("current")&&R.html(_.get("current").replace("{current}",A+1).replace("{total}",a)).show(),K[_.get("loop")||a-1>A?"show":"hide"]().html(_.get("next")),P[_.get("loop")||A?"show":"hide"]().html(_.get("previous")),ce(),_.get("preloading")&&t.each([h(-1),h(1)],function(){var i,n=W[this],o=new r(n,t.data(n,Y)),h=o.get("href");h&&s(o,h)&&(h=l(o,h),i=e.createElement("img"),i.src=h)})):O.hide(),_.get("iframe")?(n=_.get("createIframe"),_.get("scrolling")||(n.scrolling="no"),t(n).attr({src:_.get("href"),"class":Z+"Iframe"}).one("load",o).appendTo(I),ae.one(he,function(){n.src="//about:blank"}),_.get("fastIframe")&&t(n).trigger("load")):o(),"fade"===_.get("transition")?x.fadeTo(g,1,i):i())},"fade"===_.get("transition")?x.fadeTo(g,0,function(){J.position(0,d)}):J.position(g,d)}},J.next=function(){!$&&W[1]&&(_.get("loop")||W[A+1])&&(A=h(1),f(W[A]))},J.prev=function(){!$&&W[1]&&(_.get("loop")||A)&&(A=h(-1),f(W[A]))},J.close=function(){U&&!G&&(G=!0,U=!1,u(oe),_.get("onCleanup"),E.unbind("."+Z),v.fadeTo(_.get("fadeOut")||0,0),x.stop().fadeTo(_.get("fadeOut")||0,0,function(){x.hide(),v.hide(),u(he),I.remove(),setTimeout(function(){G=!1,u(re),_.get("onClosed")},1)}))},J.remove=function(){x&&(x.stop(),t[Y].close(),x.stop(!1,!0).remove(),v.remove(),G=!1,x=null,t("."+te).removeData(Y).removeClass(te),t(e).unbind("click."+Z).unbind("keydown."+Z))},J.element=function(){return t(_.el)},J.settings=X)})(jQuery,document,window);
if(typeof naviki == "undefined" || !naviki){	var naviki = {};};naviki.Lang = {	local_lang:{"NavikiException.1000.msg":"HTTP Exception serwera wyznaczania tras","NavikiException.1001.msg":"Aby wysłać żądanie wyznaczenia trasy, wprowadź miejsce przeznaczenia","NavikiException.1002.msg":"Podłączenie do serwera wyznaczania tras nie jest obecnie dostępne","NavikiException.3000.msg":"Proszę sprawdzić prawidłowość pisowni. Wprowadź adres w następujący sposób: Ulica, Miasto, Kraj","NavikiException.5012.msg":"Ta trasa jest już dostępna w Twojej aplikacji Naviki – zob. \"Zapamiętane trasy\"","NavikiException.5013.msg":"Ta trasa jest już dostępna w Twojej aplikacji Naviki – zob. \"Moje trasy\"","NavikiException.5017.msg":"Your app version is outdated. Please install update.","NavikiException.5100.msg":"Plik GPS {0} jest zbyt duży. Rozmiar maksymalny to 5000 kB.","NavikiException.5101.msg":"Naviki obsługuje pliki GPX, KML, KMZ i TCX.","NavikiException.5102.msg":"Wystąpił błąd podczas przetwarzania Twojego pliku {0}. Proszę spróbować ponownie.","NavikiException.5104.msg":"Twój plik GPS {0} nie zawiera trasy.","NavikiException.5200":"Niestety, jest to niemożliwe, ponieważ ten zespół nie istnieje","NavikiException.5201.msg":"Ten konkurs jest już zakończony","NavikiException.5204.msg":"Nie są już możliwe modyfikacje zespołu","NavikiException.5205.msg":"Niestety, jest to niemożliwe","NavikiException.5206.msg":"Nie jest to możliwe, ponieważ nie zapisałeś/aś się jeszcze do konkursu.","NavikiException.5207.msg":"Członkostwo już zostało wyjaśnione","NavikiException.5208.msg":"Nie jest to możliwe, ponieważ zapisałeś/aś się już do innego zespołu.","NavikiException.5209.msg":"Niestety, jest to niemożliwe, ponieważ ten zespół nie istnieje","NavikiException.5210.msg":"Ten zespół jest już kompletny","NavikiException.5211.msg":"Kod kuponu nie jest dostępny dla tego konkursu.","NavikiException.5212.msg":"Niestety, jest to niemożliwe, ponieważ ten zespół nie istnieje","NavikiException.5213.msg":"Nie mogę dodać trasy do konkursu, ponieważ useAllWays jest aktywowane.","NavikiException.5214.msg":"Niestety, jest to niemożliwe, ponieważ ten uczestnik wycofał się z konkursu","NavikiException.5215.msg":"Niestety, jest to niemożliwe, ponieważ ten uczestniki nie jest powiązany z zespołem","NavikiException.5219.msg":"This team name already exists. Insert a different name.","NavikiException.5300.msg":"Ten kod kuponu nie jest ważny. Proszę poprawić swój wpis.","NavikiException.5301.msg":"Ten kod był już używany tak często, że stracił ważność.","NavikiException.5302.msg":"Niestety, ten kod kuponu nie jest jeszcze albo już dostępny.","SelectPointsOfInterestAccommodation":"Noclegi","SelectPointsOfInterestActivitiesAndExperiences":"Activity / Experience","SelectPointsOfInterestAdministration":"Administracja","SelectPointsOfInterestAircraft":"Samolot","SelectPointsOfInterestApartment":"Apartament / B&B","SelectPointsOfInterestBakery":"Bakery","SelectPointsOfInterestBeachesSwimmingAndAnglingLakes":"Beach / Swimming / Angling lake","SelectPointsOfInterestBedAndBike":"Bed + Bike","SelectPointsOfInterestBedAndBreakfast":"Bed & Breakfast","SelectPointsOfInterestBicycleRental":"Bicycle rental","SelectPointsOfInterestBicycleService":"Serwis rowerowy","SelectPointsOfInterestBicycleTube":"Dętkomat","SelectPointsOfInterestBiergarten":"Ogródek piwny","SelectPointsOfInterestBikeFriend":"Bike Friends","SelectPointsOfInterestBooksMagazines":"Książki / magazyny","SelectPointsOfInterestBuildingsAndMonuments":"Building / Monument","SelectPointsOfInterestBus":"Autobus","SelectPointsOfInterestBusiness":"Biznes","SelectPointsOfInterestCableCar":"Kolejka linowa","SelectPointsOfInterestCafe":"Bar","SelectPointsOfInterestCampSite":"Camp site","SelectPointsOfInterestCamping":"Kemping","SelectPointsOfInterestCarRental":"Wypożyczalnia samochodów","SelectPointsOfInterestChargingStation":"Stacja ładowania","SelectPointsOfInterestClothing":"Odzież","SelectPointsOfInterestCompressedAir":"Sprężone powietrze","SelectPointsOfInterestCraft":"Rzemiosło","SelectPointsOfInterestCulture":"Kultura","SelectPointsOfInterestCultureHobby":"Kultura / hobby","SelectPointsOfInterestDoItYourself":"Artykuły dla majsterkowiczów","SelectPointsOfInterestDrinkingWater":"Woda pitna","SelectPointsOfInterestEducation":"Edukacja","SelectPointsOfInterestElectronics":"Elektronika","SelectPointsOfInterestEmergency":"Pogotowie","SelectPointsOfInterestEntertainment":"Rozrywka","SelectPointsOfInterestFastFood":"Bar szybkiej obsługi","SelectPointsOfInterestFerryRoutes":"Ferry route","SelectPointsOfInterestFinance":"Finanse","SelectPointsOfInterestFoodDrink":"Jedzenie / picie","SelectPointsOfInterestForChildren":"For children","SelectPointsOfInterestFuel":"Gas station","SelectPointsOfInterestFunGames":"Zabawa / gry","SelectPointsOfInterestGarageStore":"Warsztat / sklep","SelectPointsOfInterestGasStation":"Gas station","SelectPointsOfInterestGrocery":"Sklep spożywczy","SelectPointsOfInterestGuestHouse":"Guest house / B&B","SelectPointsOfInterestHealth":"Zdrowie","SelectPointsOfInterestHealthBeauty":"Zdrowie / uroda","SelectPointsOfInterestHolidayHouseAgencies":"Holiday house agency","SelectPointsOfInterestHolidayResort":"Holiday resort","SelectPointsOfInterestHostel":"Hostel","SelectPointsOfInterestHotel":"Hotel","SelectPointsOfInterestHut":"Chata","SelectPointsOfInterestIceCream":"Lodziarnia","SelectPointsOfInterestInstitution":"Instytucja","SelectPointsOfInterestInteriorFurniture":"Wnętrzarstwo / meble","SelectPointsOfInterestLeisureRecreation":"Rekreacja","SelectPointsOfInterestLocalSpecialities":"Local specialities","SelectPointsOfInterestMallSupermarket":"Supermarket","SelectPointsOfInterestMoreInfos":"Dowiedz się więcej...","SelectPointsOfInterestMuseumsGalleriesAndCrafts":"Museum / Gallery / Craft","SelectPointsOfInterestNaturalAreasParksAndGardens":"Natural area / Park / Garden","SelectPointsOfInterestNatureCamp":"Nature camp","SelectPointsOfInterestNatureLandscape":"Natura / krajobraz","SelectPointsOfInterestOrganisation":"Organizacja","SelectPointsOfInterestParking":"Parking","SelectPointsOfInterestPicnicSite":"Picnic site","SelectPointsOfInterestPlaygroundsAndOutdoorFitness":"Playground / Outdoor fitness","SelectPointsOfInterestPolice":"Policja","SelectPointsOfInterestPostalService":"Usługi pocztowe","SelectPointsOfInterestPubBar":"Pub / Bar","SelectPointsOfInterestRailway":"Koleje","SelectPointsOfInterestReligiousSite":"Miejsce kultu religijnego","SelectPointsOfInterestRenting":"Wypożyczalnia","SelectPointsOfInterestRepairAndChargingStation":"Repair / Shop","SelectPointsOfInterestRestaurant":"Restauracja","SelectPointsOfInterestShelter":"Shelter","SelectPointsOfInterestShip":"Statek","SelectPointsOfInterestShop":"Sklep","SelectPointsOfInterestSports":"Sporty","SelectPointsOfInterestSportsOutdoorTravel":"Sporty / outdoor / podróż","SelectPointsOfInterestStationeryGifts":"Artykuły papiernicze / prezenty","SelectPointsOfInterestTaxi":"Taksówki","SelectPointsOfInterestThemeParksAquaDomesAndZoos":"Theme park / Aqua dome / Zoo","SelectPointsOfInterestTourismInformation":"Informacje turystyczne","SelectPointsOfInterestTradeService":"Handel / usługi","SelectPointsOfInterestTraffic":"Transport","SelectPointsOfInterestTransportAndInformation":"Transport / Information","SelectPointsOfInterestVendingMachine":"Automat do sprzedaży","SelectPointsOfInterestWarningTooManyItems":"Możesz wybrać tylko pięć kategorie równocześnie.","SelectPointsOfInterestWellness":"Odnowa biologiczna","abc.daysToStart":"days until start <br>of the ABC!","abc.daysToStart.teaser":"All ABC leaderboards<br>at a glance ...","abc.header.kilometers":"km cycled so far in ABC %1$s","base.action":"Działania","base.add":"dodaj","base.assume":"zaakceptuj","base.attention":"Uwaga","base.bookmark":"zapamiętaj","base.by":"w","base.cancel":"anuluj","base.close":"zamknij","base.cookieChoices":"Ta witryna korzysta z plików cookie, dzięki którym możemy Ci zapewnić maksymalną wygodę korzystania z aplikacji.","base.copy":"Copy","base.deactivatedAccountChangeEmailButton":"Change e-mail address","base.deactivatedAccountHint":"Please verify your e-mail address to activate your account. We sent you a confirmation mail to {0}.","base.deactivatedAccountResendButton":"Resend activation mail","base.delete":"usuń","base.edit":"edytuj","base.error":"Błąd","base.feet":"ft","base.hour":"h","base.kilometre":"km","base.kilometre.kmh":"km/h","base.language":"Język","base.meter":"m","base.miles":"mi","base.miles.mih":"mi/h","base.more":"Dowiedz się więcej...","base.move":"przenieś","base.no":"nie","base.notsupported":"Ta funkcja nie jest obsługiwana przez Twoją przeglądarkę.","base.ok":"OK","base.photos":"Fotografie","base.print":"drukuj","base.send":"Oblicz","base.serviceNotAvailable":"Usługa niedostępna","base.store":"zapisz","base.switchDirection":"Invert direction","base.yes":"tak","contest.chooseArea":"Wybierz miasto","contest.chooseTeam":"Wybierz drużynę","contest.deletedUser":"Deleted user","contest.joinContest":"Participate","contest.joinContestHint":"Join the contest now!","contest.joinTeam":"Join team","contest.joinTeamHint":"Join a team now!","contest.leaveArea":"Opuść miasto","contest.leaveContest":"Leave contest","contest.leaveContestHint":"You are participating in the contest","contest.leaveTeam":"Opuść drużynę","contest.leaveTeamHint":"You are a member of this team","contest.linkToRegistration":"Zarejestruj się do ECC teraz ...","contest.message.confirmJoinningToTeam.message":"Gratulacje! Kapitan drużyny zaakceptował Twoje zgłoszenie. Wycieczki, które zarejestrujesz w trakcie tego wyścigu Naviki, trafią na konto Twojej drużyny.","contest.message.confirmJoinningToTeam.title":"Udało Ci się dołączyć do drużyny!","contest.message.email.regards":"Naviki, www.naviki.org","contest.message.email.salutation":"Cześć %s!","contest.message.email.teammessage.hint":"Please don't reply to this message. You can contact your team in the Naviki app. ","contest.message.email.teammessage.subject":"New message to your team from %s","contest.message.joinToTeam.message":"Kolejny rowerzysta chce dołączyć do Twojej drużyny w wyścigu Naviki. Teraz możesz otworzyć wyścig w swojej aplikacji Naviki, przejść do \"Moja drużyna\" i zatwierdzić nowego członka swojej drużyny!","contest.message.joinToTeam.title":"Ktoś chce dołączyć do Twojej drużyny!","contest.message.leaveFromTeam.message":"Właśnie wycofał się jeden z członków Twojej drużyny. Teraz w Twojej drużynie jest co najmniej jedno wolne miejsce. Chcesz zapytać innych rowerzystów, czy chcieliby dołączyć do Twojej drużyny?","contest.message.leaveFromTeam.title":"Ktoś się wycofał z Twojej drużyny.","contest.myCity":"Moje miasto","contest.myTeam":"Moja drużyna","contest.nextStep":"Kolejny krok","contest.participate.loginHint":"%1$s Log in to Naviki %2$s in order to participate!","contest.participate.welcomeAppMessage":"You are ready for the contest. Now get the Naviki app and record your bicycle trips!","contest.searchArea":"Wyszukaj swoje miasto ...","contest.searchTeam":"Wyszukaj swoją drużynę ...","contest.timePeriod.hint":"Active period already completed.","contest.timePeriod.proportional":"Active period already completed. Achieved km appear in the ranking proportionately on a daily basis.","contest.university.employees":"Employees %s","contest.university.founding":"Founded %s","contest.university.students":"Students %s","error.routeNotAllowed.msg":"The owner must change the visibility of this route so that it can be seen here.","error.routeNotAllowed.title":"Restricted visibility","error.routeNotFound.msg":"The route with given ID was not found. The owner of this route should verify that it exists.","error.routeNotFound.title":"Route not found","error.wayUidDeprecated.msg":"The URL must contain the UUID of a route.","error.wayUidDeprecated.title":"Invalid parameter","pi_map.contextMenu_centerOnThisLocation":"Wyśrodkuj na tej lokalizacji","pi_map.earthTour":"GoogleEarth","pi_map.enlarge_picture":"Powiększ zdjęcie ...","pi_map.error.web_gl_disabled.msg":"Naviki could not load the map (Mapbox GL). Maybe your browser does not support WebGL or it is disabled. Please make sure that your web browser is up to date and WebGL is enabled or try another browser.","pi_map.error.web_gl_disabled.title":"Could not load map","pi_map.fullscreen_max":"Maksymalizuj mapę","pi_map.fullscreen_min":"Minimalizuj mapę","pi_map.geolocation":"Wyświetl swoją pozycję","pi_map.geolocation.missing_permission":"Authorise Naviki to use your location in browser settings.","pi_map.landingpage_layer.body":"Dowiedz się więcej na stronie Naviki","pi_map.landingpage_layer.title":"%s: Więcej informacji o jeździe na rowerze","pi_map.more":"więcej...","pi_map.privacy_policy":"Prywatność","pi_map.stops_layer_from":"Informacje o wycieczce z tego miejsca","pi_map.stops_layer_to":"Informacja o wycieczce do tego miejsca","pi_map.terms_of_use":"Terms of use","pi_map.type.3d":"Muscle Ache","pi_map.type.3d_bicycle":"Muscle Ache Plus","pi_map.type.alternative":"Chain Oil","pi_map.type.alternative_bicycle":"Chain Oil Plus","pi_map.type.default":"Tailwind","pi_map.type.default_bicycle":"Tailwind Plus","pi_map.type.enable_bike_highlighting":"Highlight official bike routes and numbered nodes","pi_map.type.google.satellite":"Satelita","pi_map.type.osm":"OSM","pi_map.type.osm_bike":"Rower OSM","pi_map_open_app.button":"Start Naviki App","pi_portlet.allWays.button.filter.calculated":"Oblicz","pi_portlet.allWays.button.filter.others":"Other source","pi_portlet.allWays.button.filter.recorded":"Recorded","pi_portlet.allWays.button.sort":"Sort ...","pi_portlet.allWays.button.sort.dateLastChange":"Date of last change","pi_portlet.allWays.button.sort.dateOrigin":"Date of creation","pi_portlet.allWays.button.sort.length":"Długość","pi_portlet.allWays.footer":"This is the space for your calculated, recorded and imported routes","pi_portlet.myWays":"Moje własne trasy","pi_portlet.myWaysFooter":"Ta przestrzeń jest przeznaczona na trasy zarejestrowane przez Ciebie przy użyciu aplikacji Naviki","pi_portlet.wayBookmarks":"Moje zapamiętane trasy","pi_portlet.wayBookmarksFooter":"Ta przestrzeń jest przeznaczona na trasy, oznaczone przez Ciebie jako \"zapamiętane\"","pi_routing_request.add_destination":"Miejsce przeznaczenia trasy","pi_routing_request.add_start":"Trasa stąd","pi_routing_request.add_target":"Dodaj miejsce przeznaczenia","pi_routing_request.attention.address_not_found":"Pamiętaj o prawidłowej pisowni i wprowadzaniu adresów w następujący sposób: Ulica, Miasto, Kraj","pi_routing_request.attention.empty_inputfields":"Wypełnij wszystkie pola!","pi_routing_request.attention.no_route_between_points":"Naviki nie może wyliczyć żadnej trasy między tymi lokalizacjami. Spróbuj ponownie, korzystając z innych lokalizacji.","pi_routing_request.attention.routing_not_possible":"Naviki nie może tymczasowo wyliczyć żadnej trasy. Spróbuj ponownie później.","pi_routing_request.close_to_roundtrip":"Wycieczka tam i z powrotem","pi_routing_request.current_location":"Wyświetl swoją pozycję","pi_routing_request.default":"Ulica, numer domu, miejscowość","pi_routing_request.destination":"Miejsce przeznaczenia","pi_routing_request.imbed":"osadź","pi_routing_request.start":"Start","pi_routing_request.submit":"Oblicz","pi_routing_widget.header":"Wylicz trasę rowerową","pi_routing_widget.start":"Start","pi_routing_widget.target":"Miejsce przeznaczenia","pi_search":"Szukaj","pi_search.hint":"Lokalizacja lub wyszukiwane słowo","pi_search.invalid":"Nie wprowadzaj znaków specjalnych (*+:;%$Â§...)!","pi_search.no_result":"No result found for search term","pi_search.to_short":"Wprowadź dłuższe wyszukiwane słowo!","pi_userprofile":"Profil użytkownika Naviki","pi_userprofile.co2.engineType":"Typ zużycia","pi_userprofile.co2.gasconsumption":"średnie zużycie na 100 km","pi_userprofile.co2.gasconsumption_not_valid":"Wprowadź realistyczną wartość z przedziału od 3 do 20 l.","pi_userprofile.co2.grams_per_kilometer":"g/km","pi_userprofile.co2.header":"Mój samochód","pi_userprofile.co2.individual_co2_per_km":"CO2 emission","pi_userprofile.co2.individual_co2_per_km_not_valid":"Insert a realistic value.","pi_userprofile.co2.liter":"litry","pi_userprofile.co2.vehicleClass":"Pojazd","pi_userprofile.delete":"Usuń profil","pi_userprofile.deleteDialog":"Naprawdę chcesz usunąć swój profil?","pi_userprofile.disable":"Deactivate account","pi_userprofile.disableDialog":"Are you sure, you want to disable your profile? Your data will be deleted after 30 days. Within this time you can reactivate your account via login.","pi_userprofile.email":"E-mail","pi_userprofile.email_already_exists":"Ten adres e-mail jest już używany dla innego konta w Naviki.","pi_userprofile.email_change_hint":"We have sent you a confirmation email. Click the link in that email. Afterwards your updated email address will appear here.","pi_userprofile.email_not_valid":"Ten adres e-mail jest nieprawidłowy.","pi_userprofile.energy.kilocalories":"kcal","pi_userprofile.energy.kilojoule":"kJ","pi_userprofile.engine.diesel":"diesel","pi_userprofile.engine.gasoline":"benzyna","pi_userprofile.engine.naturalgas":"gaz ziemny","pi_userprofile.general":"Informacje ogólne","pi_userprofile.image":"Fotografia","pi_userprofile.logoutDialog":"Really want to log out?","pi_userprofile.mybike.header":"Mój rower","pi_userprofile.mybike.type":"Bicycle type","pi_userprofile.mybike.type.bike_without_drive_support":"Ordinary bicycle","pi_userprofile.mybike.type.e_bike":"E-bike","pi_userprofile.mybike.type.s_pedelec":"S-pedelec","pi_userprofile.name":"Nazwa","pi_userprofile.newsletter":"Newsletter Naviki przesyłany e-mailem","pi_userprofile.notifications.header":"Powiadomienia","pi_userprofile.personal.age":"Wiek","pi_userprofile.personal.average_speed":"Średnia prędkość","pi_userprofile.personal.average_speed.sub":"Enter the average speed at which you cycle without obstacles. When you plan routes, Naviki considers this speed as well as the influence of traffic lights and gradients, to predict the average speed on these routes.","pi_userprofile.personal.average_speed_not_valid":"Wprowadź realistyczną wartość między 4 a 100 km/godz.","pi_userprofile.personal.birthday":"Data urodzenia","pi_userprofile.personal.birthday_not_valid":"Wprowadź realistyczny wiek między 2 a 110 lat","pi_userprofile.personal.gender":"Płeć","pi_userprofile.personal.gender.diverse":"diverse","pi_userprofile.personal.gender.female":"żeńska","pi_userprofile.personal.gender.male":"męska","pi_userprofile.personal.header":"Prywatne","pi_userprofile.personal.size":"Wzrost","pi_userprofile.personal.size_not_valid":"Wprowadź realistyczny wzrost między 80 a 240 cm","pi_userprofile.personal.weight":"Masa ciała","pi_userprofile.personal.weight_not_valid":"Wprowadź realistyczną wartość między 10 a 180 kg","pi_userprofile.privacy.header":"Prywatność","pi_userprofile.privacy.profile":"Zgadzam się na udostępnienie innym użytkownikom Naviki takich informacji jak moje zdjęcie profilowe, moja nazwa użytkownika i moje publiczne trasy.","pi_userprofile.privacy.profile.sub":"If enabled, other Naviki users can see your nickname and profile image.","pi_userprofile.privacy.profile.title":"Profile","pi_userprofile.privacy.ways":"I agree that other Naviki users can find my routes.","pi_userprofile.privacy.ways.button.all.private":"Make all routes visible only for me","pi_userprofile.privacy.ways.button.all.private.confirm":"Now really set the visibility of all routes to 'Only me'? Afterwards, even routes that have already been shared via a link or widget will no longer be visible to others.","pi_userprofile.privacy.ways.button.all.public":"Make all routes visible for anyone","pi_userprofile.privacy.ways.button.all.public.confirm":"Do you really want all routes to be visible for 'Anyone'?","pi_userprofile.privacy.ways.button.all.shared":"Make all routes visible for people with the link","pi_userprofile.privacy.ways.button.all.shared.confirm":"Do you really want to set the visibility of all routes to 'People with the link' now?","pi_userprofile.privacy.ways.button.all.success":"All routes updated successfully","pi_userprofile.privacy.ways.options.title":"Who can see my new routes?","pi_userprofile.privacy.ways.sub":"Use the buttons to set who can see your future recorded or saved routes. Use the following links to adjust the visibility of your existing routes.","pi_userprofile.privacy.ways.title":"Visibility of routes","pi_userprofile.units.energy":"Energia","pi_userprofile.units.energy.kilocalories":"Kilocalories (kcal)","pi_userprofile.units.energy.kilojoules":"Kilojoules (kJ)","pi_userprofile.units.header":"Units","pi_userprofile.units.length":"Distance","pi_userprofile.units.length.kilometers":"Metric (kilometers)","pi_userprofile.units.length.miles":"Imperial (miles)","pi_userprofile.vehicle.costs":"Koszty auta","pi_userprofile.vehicle.costs.unit":"Cent/km","pi_userprofile.vehicle.individual":"indywidualne dane","pi_userprofile.vehicle.middle_class":"Medium class car","pi_userprofile.vehicle.middle_class_diesel":"Klasa średnia (diesel 174 g CO2/km)","pi_userprofile.vehicle.middle_class_gasoline":"Klasa średnia (benzyniak 216 g CO2/km)","pi_userprofile.vehicle.not_available":"Nie mam samochodu","pi_userprofile.vehicle.small_car":"Small car","pi_userprofile.vehicle.small_car_diesel":"Małe auto (diesel 125 g CO2/km)","pi_userprofile.vehicle.small_car_gasoline":"Małe auto (benzyniak 160 g CO2/km)","pi_userprofile.vehicle.sub":"Przy pomocy tych danych Naviki obliczy Twoje osobiste oszczędności paliwa oraz redukcję globalnej emisji CO2 dzięki jeździe na rowerze.","pi_userprofile.vehicle.upper_class":"Upper class car","pi_userprofile.vehicle.upper_class_diesel":"Klasa wyższa (diesel 229 g CO2/km)","pi_userprofile.vehicle.upper_class_gasoline":"Klasa wyższa (benzyniak 271 g CO2/km)","pi_way.crdate":"Wycieczka odbyta w dniu","pi_way.delete_path_question":"Naprawdę chcesz usunąć tę trasę?","pi_way.description":"Opis","pi_way.env":"Otoczenie","pi_way.env_city":"Miasto/miasteczko","pi_way.env_forest":"Las","pi_way.env_hills":"Niskie góry","pi_way.env_industry":"Przemysł","pi_way.env_meadow":"Łąki/pola","pi_way.env_mountains":"Wysokie góry","pi_way.env_river":"Rzeka/jezioro","pi_way.env_sea":"Grobla/wybrzeże","pi_way.env_village":"Teren rolniczy/wsie","pi_way.middle_traffic_flow":"Średnie natężenie ruchu","pi_way.middle_traffic_flow_very_high":"bardzo duże","pi_way.middle_traffic_flow_very_low":"bardzo małe","pi_way.private_way":"Status","pi_way.private_way.private":"prywatna","pi_way.private_way.public":"publiczna","pi_way.private_way.shared":"People with the link","pi_way.recorded":"How was the route created?","pi_way.recorded.other":"Other source","pi_way.recorded.self":"Cycled and recorded by myself","pi_way.special_usage":"Rodzaj użytkowania","pi_way.special_usage_children":"Dzieci","pi_way.special_usage_m_t_b":"Rower górski","pi_way.special_usage_racing":"Rower wyścigowy","pi_way.special_usage_seniors":"Seniorzy","pi_way.special_usage_sparetime":"Rekreacja","pi_way.special_usage_tourism":"Turystyka","pi_way.title":"Nazwa","pi_way_info.bookmark_path_name":"Nazwa","pi_way_info.calories_burned":"Spalone kalorie","pi_way_info.co2_background_info":"Informacje w tle","pi_way_info.co2_savings":"Redukcja emisji CO2","pi_way_info.download_gpx_text":"(np. Garmin, Magellan)","pi_way_info.download_header":"Pliki GPS","pi_way_info.download_kml_paths_text":"(as paths, e.g. for editing in Google Earth)","pi_way_info.download_kml_text":"(np. Google Earth)","pi_way_info.download_ovl_text":"(np. mapy Top50)","pi_way_info.download_tcx_text":"(np. Garmin Training Center)","pi_way_info.heightProfileLabelX":"Długość w","pi_way_info.heightProfileLabelY":"Wysokość w","pi_way_info.infoheightdiff":"Przewyższenie","pi_way_info.infoheightmax":"Najwyższy punkt","pi_way_info.infoheightmin":"Najniższy punkt","pi_way_info.infokm":"Długość","pi_way_info.infokmtime":"Czas jazdy przy %s km/h","pi_way_info.menu_bookmark":"Zapamiętaj","pi_way_info.menu_delete":"Usuń","pi_way_info.menu_download":"Pobranie danych GPS","pi_way_info.menu_edit":"Edytuj","pi_way_info.menu_print":"Podgląd wydruku","pi_way_info.menu_rating":"Oceń","pi_way_info.menu_report":"Zgłoś","pi_way_info.no_description":"Jeszcze nie ma opisu.","pi_way_info.no_images":"Jeszcze nie ma zdjęć.","pi_way_info.ownerprofile_private":"Użytkownik nie podaje tych informacji jako publicznych.","pi_way_info.price_savings":"Koszty auta","pi_way_info.print_info":"Naviki tworzy Twój podgląd wydruku. To może trochę potrwać, zależnie od długości trasy.","pi_way_info.rating":"Rating","pi_way_info.rating.1":"bardzo zła","pi_way_info.rating.2":"zła","pi_way_info.rating.3":"średnia","pi_way_info.rating.4":"dobra","pi_way_info.rating.5":"bardzo dobra","pi_way_info.rating_header":"Rating trasy","pi_way_info.report_header":"Raport z trasy","pi_way_info.starting_points_title":"Starting points","pi_way_info.tab_description":"Informacje","pi_way_info.tab_energy":"Energia","pi_way_info.tab_images":"Ilustracje","pi_way_info.tab_overview":"Przegląd","pi_way_info.tab_owner":"Właściciel","pi_way_info.tab_profile":"Wysokości","pi_way_info.unsuited":"Raporty","pi_way_info.unsuitedforbicycles_1":"Zgłosiłeś tę trasę jako niemożliwą do przebycia","pi_way_info.unsuitedforbicycles_2":"Zgłoś tę trasę jako niemożliwą do przebycia","pi_way_info.user_profile":"Więcej tras z %s ...","pi_way_info.widget_more":"More routes on","pi_way_preview.toMapview":"Do widoku mapy ...","tx_naviki_domain_model_bicyclemarkingappointment":"Spotkanie","tx_naviki_domain_model_bicyclemarkingappointment.captcha_response":"Proszę wprowadzić słowo pokazane na ilustracji.","tx_naviki_domain_model_bicyclemarkingappointment.city":"Miejsce zamieszkania","tx_naviki_domain_model_bicyclemarkingappointment.color":"Kolor","tx_naviki_domain_model_bicyclemarkingappointment.date_of_birth":"Data urodzenia","tx_naviki_domain_model_bicyclemarkingappointment.email":"E-mail","tx_naviki_domain_model_bicyclemarkingappointment.forename":"Imię i nazwisko","tx_naviki_domain_model_bicyclemarkingappointment.framenumber":"Numer","tx_naviki_domain_model_bicyclemarkingappointment.gearing":"Przekładnia","tx_naviki_domain_model_bicyclemarkingappointment.manufacturer":"Typ","tx_naviki_domain_model_bicyclemarkingappointment.notes":"Charakterystyka specjalna","tx_naviki_domain_model_bicyclemarkingappointment.start":"Spotkanie","tx_naviki_domain_model_bicyclemarkingappointment.steering":"Kierownica","tx_naviki_domain_model_bicyclemarkingappointment.street":"Ulica / nr","tx_naviki_domain_model_bicyclemarkingappointment.surname":"Imię i nazwisko","tx_naviki_domain_model_bicyclemarkingappointment.zip":"Kod pocztowy","tx_naviki_domain_model_bicyclemarkingevent":"Impreza rowerowa","tx_naviki_domain_model_bicyclemarkingevent.name":"Nazwa","tx_naviki_domain_model_bicyclemarkingevent.periods":"Okresy","tx_naviki_domain_model_bicyclemarkingevent.time_slot_length":"Przeznaczony czas","tx_naviki_domain_model_bicyclemarkingperiod":"Okres","tx_naviki_domain_model_bicyclemarkingperiod.appointments":"Spotkania","tx_naviki_domain_model_bicyclemarkingperiod.end":"Koniec","tx_naviki_domain_model_bicyclemarkingperiod.start":"Początek","tx_naviki_pibicyclemarkingappointment.calendar.awarded":"zajęta","tx_naviki_pibicyclemarkingappointment.calendar.free":"wolna","tx_naviki_pibicyclemarkingappointment.calendar.weekdays":"Poniedziałek,wtorek,środa,czwartek,piątek,sobota,niedziela","tx_naviki_pibicyclemarkingappointment.choose_date":"Wybierz datę","tx_naviki_pibicyclemarkingappointment.dialog":"Czy chciał(a)być się teraz zarejestrować na kodowanie roweru?","tx_naviki_pibicyclemarkingappointment.dialog.back":"Z powrotem do formularza","tx_naviki_pibicyclemarkingappointment.dialog.submit":"Zapisz się","tx_naviki_pibicyclemarkingappointment.dialog.title":"Rejestracja","tx_naviki_pibicyclemarkingappointment.error.1221559976":"Ten adres e-mail jest nieważny.","tx_naviki_pibicyclemarkingappointment.error.1221560718":"Proszę wypełnić poniższe pole","tx_naviki_pibicyclemarkingappointment.error.1221563685":"Dozwolone są tylko cyfry (0-9).","tx_naviki_pibicyclemarkingappointment.error.1238108067":"Wprowadź swój kod pocztowy złożony z pięciu cyfr.","tx_naviki_pibicyclemarkingappointment.error.1238108069":"Maksymalnie 256 znaków.","tx_naviki_pibicyclemarkingappointment.error.1398857830":"Spotkanie jest już zarezerwowane.","tx_naviki_pibicyclemarkingappointment.error.1398857831":"Zostałeś/aś już zarejestrowany/a na to spotkanie.","tx_naviki_pibicyclemarkingappointment.my_bike":"Mój rower","tx_naviki_pibicyclemarkingappointment.personal_information":"Informacje osobowe","tx_naviki_pibicyclemarkingappointment.required_fields":"Pola oznaczone gwiazdką (*) muszą być wypełnione","tx_naviki_pibicyclemarkingappointment.submit":"Zaloguj się","tx_naviki_pimap.imprint":"Nota prawna (Naviki)","tx_naviki_pimapcontrol.bookmark.btn":"Zapamiętaj trasę i wykorzystaj ją w aplikacji","tx_naviki_pimapcontrol.bookmark.btn.active":"Zapamiętaj","tx_naviki_pimapcontrol.bookmark.dlg.success":"Ta trasa jest już dostępna w Twojej aplikacji Naviki – zob. \"Moje trasy\"","tx_naviki_pimapcontrol.bookmark.hint.officialRoute":"The title of official routes cannot be changed","tx_naviki_pimapcontrol.bookmark.tip":"Use in Naviki app","tx_naviki_pimapcontrol.download.tip":"Download GPS file","tx_naviki_pimapcontrol.edit.tip":"Edit route","tx_naviki_pimapcontrol.embed.dlg.code":"Embed route in your website","tx_naviki_pimapcontrol.embed.dlg.type.div":"div (recommended)","tx_naviki_pimapcontrol.embed.dlg.type.iframe":"iframe","tx_naviki_pimapcontrol.embed.tip":"Embed on website","tx_naviki_pimapcontrol.official.tip":"Quality assured official route","tx_naviki_pimapcontrol.profile.cadence":"Tempo [1/min]","tx_naviki_pimapcontrol.profile.duration":"Czas trwania [h]","tx_naviki_pimapcontrol.profile.height":"Wysokość [m]","tx_naviki_pimapcontrol.profile.height.imperial":"Wysokość [ft]","tx_naviki_pimapcontrol.profile.length":"Długość [km]","tx_naviki_pimapcontrol.profile.length.imperial":"Długość [mi]","tx_naviki_pimapcontrol.profile.pulse":"Tętno [uderzeń/min]","tx_naviki_pimapcontrol.profile.speed":"Prędkość [mk/godz.]","tx_naviki_pimapcontrol.profile.speed.imperial":"Prędkość [mi/godz.]","tx_naviki_pimapcontrol.search.list.header":"Trasy","tx_naviki_pimapcontrol.search.placeholder":"Miasto, pejzaż, rzeka ...","tx_naviki_pimapcontrol.search.tip":"Znajdź trasy innych użytkowników i trasy oficjalne","tx_naviki_piwidget.widget.bookmark.btn":"Use route with Naviki"	},	getLL:function(key){		if(key in naviki.Lang.local_lang){			return naviki.Lang.local_lang[key];		}		return key;	}};
/*
 * jQuery Nivo Slider v3.2
 * http://nivo.dev7studios.com
 *
 * Copyright 2012, Dev7studios
 * Free to use and abuse under the MIT license.
 * http://www.opensource.org/licenses/mit-license.php
 */

(function(e){var t=function(t,n){var r=e.extend({},e.fn.nivoSlider.defaults,n);var i={currentSlide:0,currentImage:"",totalSlides:0,running:false,paused:false,stop:false,controlNavEl:false};var s=e(t);s.data("nivo:vars",i).addClass("nivoSlider");var o=s.children();o.each(function(){var t=e(this);var n="";if(!t.is("img")){if(t.is("a")){t.addClass("nivo-imageLink");n=t}t=t.find("img:first")}var r=r===0?t.attr("width"):t.width(),s=s===0?t.attr("height"):t.height();if(n!==""){n.css("display","none")}t.css("display","none");i.totalSlides++});if(r.randomStart){r.startSlide=Math.floor(Math.random()*i.totalSlides)}if(r.startSlide>0){if(r.startSlide>=i.totalSlides){r.startSlide=i.totalSlides-1}i.currentSlide=r.startSlide}if(e(o[i.currentSlide]).is("img")){i.currentImage=e(o[i.currentSlide])}else{i.currentImage=e(o[i.currentSlide]).find("img:first")}if(e(o[i.currentSlide]).is("a")){e(o[i.currentSlide]).css("display","block")}var u=e("<img/>").addClass("nivo-main-image");u.attr("src",i.currentImage.attr("src")).show();s.append(u);e(window).resize(function(){s.children("img").width(s.width());u.attr("src",i.currentImage.attr("src"));u.stop().height("auto");e(".nivo-slice").remove();e(".nivo-box").remove()});s.append(e('<div class="nivo-caption"></div>'));var a=function(t){var n=e(".nivo-caption",s);if(i.currentImage.attr("title")!=""&&i.currentImage.attr("title")!=undefined){var r=i.currentImage.attr("title");if(r.substr(0,1)=="#")r=e(r).html();if(n.css("display")=="block"){setTimeout(function(){n.html(r)},t.animSpeed)}else{n.html(r);n.stop().fadeIn(t.animSpeed)}}else{n.stop().fadeOut(t.animSpeed)}};a(r);var f=0;if(!r.manualAdvance&&o.length>1){f=setInterval(function(){d(s,o,r,false)},r.pauseTime)}if(r.directionNav){s.append('<div class="nivo-directionNav"><a class="nivo-prevNav">'+r.prevText+'</a><a class="nivo-nextNav">'+r.nextText+"</a></div>");e(s).on("click","a.nivo-prevNav",function(){if(i.running){return false}clearInterval(f);f="";i.currentSlide-=2;d(s,o,r,"prev")});e(s).on("click","a.nivo-nextNav",function(){if(i.running){return false}clearInterval(f);f="";d(s,o,r,"next")})}if(r.controlNav){i.controlNavEl=e('<div class="nivo-controlNav"></div>');s.after(i.controlNavEl);for(var l=0;l<o.length;l++){if(r.controlNavThumbs){i.controlNavEl.addClass("nivo-thumbs-enabled");var c=o.eq(l);if(!c.is("img")){c=c.find("img:first")}if(c.attr("data-thumb"))i.controlNavEl.append('<a class="nivo-control" rel="'+l+'"><img src="'+c.attr("data-thumb")+'" alt="" /></a>')}else{i.controlNavEl.append('<a class="nivo-control" rel="'+l+'">'+(l+1)+"</a>")}}e("a:eq("+i.currentSlide+")",i.controlNavEl).addClass("active");e("a",i.controlNavEl).bind("click",function(){if(i.running)return false;if(e(this).hasClass("active"))return false;clearInterval(f);f="";u.attr("src",i.currentImage.attr("src"));i.currentSlide=e(this).attr("rel")-1;d(s,o,r,"control")})}if(r.pauseOnHover){s.hover(function(){i.paused=true;clearInterval(f);f=""},function(){i.paused=false;if(f===""&&!r.manualAdvance){f=setInterval(function(){d(s,o,r,false)},r.pauseTime)}})}s.bind("nivo:animFinished",function(){u.attr("src",i.currentImage.attr("src"));i.running=false;e(o).each(function(){if(e(this).is("a")){e(this).css("display","none")}});if(e(o[i.currentSlide]).is("a")){e(o[i.currentSlide]).css("display","block")}if(f===""&&!i.paused&&!r.manualAdvance){f=setInterval(function(){d(s,o,r,false)},r.pauseTime)}r.afterChange.call(this)});var h=function(t,n,r){if(e(r.currentImage).parent().is("a"))e(r.currentImage).parent().css("display","block");e('img[src="'+r.currentImage.attr("src")+'"]',t).not(".nivo-main-image,.nivo-control img").width(t.width()).css("visibility","hidden").show();var i=e('img[src="'+r.currentImage.attr("src")+'"]',t).not(".nivo-main-image,.nivo-control img").parent().is("a")?e('img[src="'+r.currentImage.attr("src")+'"]',t).not(".nivo-main-image,.nivo-control img").parent().height():e('img[src="'+r.currentImage.attr("src")+'"]',t).not(".nivo-main-image,.nivo-control img").height();for(var s=0;s<n.slices;s++){var o=Math.round(t.width()/n.slices);if(s===n.slices-1){t.append(e('<div class="nivo-slice" name="'+s+'"><img src="'+r.currentImage.attr("src")+'" style="position:absolute; width:'+t.width()+"px; height:auto; display:block !important; top:0; left:-"+(o+s*o-o)+'px;" /></div>').css({left:o*s+"px",width:t.width()-o*s+"px",height:i+"px",opacity:"0",overflow:"hidden"}))}else{t.append(e('<div class="nivo-slice" name="'+s+'"><img src="'+r.currentImage.attr("src")+'" style="position:absolute; width:'+t.width()+"px; height:auto; display:block !important; top:0; left:-"+(o+s*o-o)+'px;" /></div>').css({left:o*s+"px",width:o+"px",height:i+"px",opacity:"0",overflow:"hidden"}))}}e(".nivo-slice",t).height(i);u.stop().animate({height:e(r.currentImage).height()},n.animSpeed)};var p=function(t,n,r){if(e(r.currentImage).parent().is("a"))e(r.currentImage).parent().css("display","block");e('img[src="'+r.currentImage.attr("src")+'"]',t).not(".nivo-main-image,.nivo-control img").width(t.width()).css("visibility","hidden").show();var i=Math.round(t.width()/n.boxCols),s=Math.round(e('img[src="'+r.currentImage.attr("src")+'"]',t).not(".nivo-main-image,.nivo-control img").height()/n.boxRows);for(var o=0;o<n.boxRows;o++){for(var a=0;a<n.boxCols;a++){if(a===n.boxCols-1){t.append(e('<div class="nivo-box" name="'+a+'" rel="'+o+'"><img src="'+r.currentImage.attr("src")+'" style="position:absolute; width:'+t.width()+"px; height:auto; display:block; top:-"+s*o+"px; left:-"+i*a+'px;" /></div>').css({opacity:0,left:i*a+"px",top:s*o+"px",width:t.width()-i*a+"px"}));e('.nivo-box[name="'+a+'"]',t).height(e('.nivo-box[name="'+a+'"] img',t).height()+"px")}else{t.append(e('<div class="nivo-box" name="'+a+'" rel="'+o+'"><img src="'+r.currentImage.attr("src")+'" style="position:absolute; width:'+t.width()+"px; height:auto; display:block; top:-"+s*o+"px; left:-"+i*a+'px;" /></div>').css({opacity:0,left:i*a+"px",top:s*o+"px",width:i+"px"}));e('.nivo-box[name="'+a+'"]',t).height(e('.nivo-box[name="'+a+'"] img',t).height()+"px")}}}u.stop().animate({height:e(r.currentImage).height()},n.animSpeed)};var d=function(t,n,r,i){var s=t.data("nivo:vars");if(s&&s.currentSlide===s.totalSlides-1){r.lastSlide.call(this)}if((!s||s.stop)&&!i){return false}r.beforeChange.call(this);if(!i){u.attr("src",s.currentImage.attr("src"))}else{if(i==="prev"){u.attr("src",s.currentImage.attr("src"))}if(i==="next"){u.attr("src",s.currentImage.attr("src"))}}s.currentSlide++;if(s.currentSlide===s.totalSlides){s.currentSlide=0;r.slideshowEnd.call(this)}if(s.currentSlide<0){s.currentSlide=s.totalSlides-1}if(e(n[s.currentSlide]).is("img")){s.currentImage=e(n[s.currentSlide])}else{s.currentImage=e(n[s.currentSlide]).find("img:first")}if(r.controlNav){e("a",s.controlNavEl).removeClass("active");e("a:eq("+s.currentSlide+")",s.controlNavEl).addClass("active")}a(r);e(".nivo-slice",t).remove();e(".nivo-box",t).remove();var o=r.effect,f="";if(r.effect==="random"){f=new Array("sliceDownRight","sliceDownLeft","sliceUpRight","sliceUpLeft","sliceUpDown","sliceUpDownLeft","fold","fade","boxRandom","boxRain","boxRainReverse","boxRainGrow","boxRainGrowReverse");o=f[Math.floor(Math.random()*(f.length+1))];if(o===undefined){o="fade"}}if(r.effect.indexOf(",")!==-1){f=r.effect.split(",");o=f[Math.floor(Math.random()*f.length)];if(o===undefined){o="fade"}}if(s.currentImage.attr("data-transition")){o=s.currentImage.attr("data-transition")}s.running=true;var l=0,c=0,d="",m="",g="",y="";if(o==="sliceDown"||o==="sliceDownRight"||o==="sliceDownLeft"){h(t,r,s);l=0;c=0;d=e(".nivo-slice",t);if(o==="sliceDownLeft"){d=e(".nivo-slice",t)._reverse()}d.each(function(){var n=e(this);n.css({top:"0px"});if(c===r.slices-1){setTimeout(function(){n.animate({opacity:"1.0"},r.animSpeed,"",function(){t.trigger("nivo:animFinished")})},100+l)}else{setTimeout(function(){n.animate({opacity:"1.0"},r.animSpeed)},100+l)}l+=50;c++})}else if(o==="sliceUp"||o==="sliceUpRight"||o==="sliceUpLeft"){h(t,r,s);l=0;c=0;d=e(".nivo-slice",t);if(o==="sliceUpLeft"){d=e(".nivo-slice",t)._reverse()}d.each(function(){var n=e(this);n.css({bottom:"0px"});if(c===r.slices-1){setTimeout(function(){n.animate({opacity:"1.0"},r.animSpeed,"",function(){t.trigger("nivo:animFinished")})},100+l)}else{setTimeout(function(){n.animate({opacity:"1.0"},r.animSpeed)},100+l)}l+=50;c++})}else if(o==="sliceUpDown"||o==="sliceUpDownRight"||o==="sliceUpDownLeft"){h(t,r,s);l=0;c=0;var b=0;d=e(".nivo-slice",t);if(o==="sliceUpDownLeft"){d=e(".nivo-slice",t)._reverse()}d.each(function(){var n=e(this);if(c===0){n.css("top","0px");c++}else{n.css("bottom","0px");c=0}if(b===r.slices-1){setTimeout(function(){n.animate({opacity:"1.0"},r.animSpeed,"",function(){t.trigger("nivo:animFinished")})},100+l)}else{setTimeout(function(){n.animate({opacity:"1.0"},r.animSpeed)},100+l)}l+=50;b++})}else if(o==="fold"){h(t,r,s);l=0;c=0;e(".nivo-slice",t).each(function(){var n=e(this);var i=n.width();n.css({top:"0px",width:"0px"});if(c===r.slices-1){setTimeout(function(){n.animate({width:i,opacity:"1.0"},r.animSpeed,"",function(){t.trigger("nivo:animFinished")})},100+l)}else{setTimeout(function(){n.animate({width:i,opacity:"1.0"},r.animSpeed)},100+l)}l+=50;c++})}else if(o==="fade"){h(t,r,s);m=e(".nivo-slice:first",t);m.css({width:t.width()+"px"});m.animate({opacity:"1.0"},r.animSpeed*2,"",function(){t.trigger("nivo:animFinished")})}else if(o==="slideInRight"){h(t,r,s);m=e(".nivo-slice:first",t);m.css({width:"0px",opacity:"1"});m.animate({width:t.width()+"px"},r.animSpeed*2,"",function(){t.trigger("nivo:animFinished")})}else if(o==="slideInLeft"){h(t,r,s);m=e(".nivo-slice:first",t);m.css({width:"0px",opacity:"1",left:"",right:"0px"});m.animate({width:t.width()+"px"},r.animSpeed*2,"",function(){m.css({left:"0px",right:""});t.trigger("nivo:animFinished")})}else if(o==="boxRandom"){p(t,r,s);g=r.boxCols*r.boxRows;c=0;l=0;y=v(e(".nivo-box",t));y.each(function(){var n=e(this);if(c===g-1){setTimeout(function(){n.animate({opacity:"1"},r.animSpeed,"",function(){t.trigger("nivo:animFinished")})},100+l)}else{setTimeout(function(){n.animate({opacity:"1"},r.animSpeed)},100+l)}l+=20;c++})}else if(o==="boxRain"||o==="boxRainReverse"||o==="boxRainGrow"||o==="boxRainGrowReverse"){p(t,r,s);g=r.boxCols*r.boxRows;c=0;l=0;var w=0;var E=0;var S=[];S[w]=[];y=e(".nivo-box",t);if(o==="boxRainReverse"||o==="boxRainGrowReverse"){y=e(".nivo-box",t)._reverse()}y.each(function(){S[w][E]=e(this);E++;if(E===r.boxCols){w++;E=0;S[w]=[]}});for(var x=0;x<r.boxCols*2;x++){var T=x;for(var N=0;N<r.boxRows;N++){if(T>=0&&T<r.boxCols){(function(n,i,s,u,a){var f=e(S[n][i]);var l=f.width();var c=f.height();if(o==="boxRainGrow"||o==="boxRainGrowReverse"){f.width(0).height(0)}if(u===a-1){setTimeout(function(){f.animate({opacity:"1",width:l,height:c},r.animSpeed/1.3,"",function(){t.trigger("nivo:animFinished")})},100+s)}else{setTimeout(function(){f.animate({opacity:"1",width:l,height:c},r.animSpeed/1.3)},100+s)}})(N,T,l,c,g);c++}T--}l+=100}}};var v=function(e){for(var t,n,r=e.length;r;t=parseInt(Math.random()*r,10),n=e[--r],e[r]=e[t],e[t]=n);return e};var m=function(e){if(this.console&&typeof console.log!=="undefined"){console.log(e)}};this.stop=function(){if(!e(t).data("nivo:vars").stop){e(t).data("nivo:vars").stop=true;m("Stop Slider")}};this.start=function(){if(e(t).data("nivo:vars").stop){e(t).data("nivo:vars").stop=false;m("Start Slider")}};r.afterLoad.call(this);return this};e.fn.nivoSlider=function(n){return this.each(function(r,i){var s=e(this);if(s.data("nivoslider")){return s.data("nivoslider")}var o=new t(this,n);s.data("nivoslider",o)})};e.fn.nivoSlider.defaults={effect:"random",slices:15,boxCols:8,boxRows:4,animSpeed:500,pauseTime:3e3,startSlide:0,directionNav:true,controlNav:true,controlNavThumbs:false,pauseOnHover:true,manualAdvance:false,prevText:"Prev",nextText:"Next",randomStart:false,beforeChange:function(){},afterChange:function(){},slideshowEnd:function(){},lastSlide:function(){},afterLoad:function(){}};e.fn._reverse=[].reverse})(jQuery);
/**
 * 
 */
;(function($) {
	$( document ).ready(function() {

		// for each teaser-box: add link from header also to content text
		$("#teaser .teaser-box").each(function() {
			const teaserBox = $(this);
			const href = teaserBox.find("header a:first").attr("href");
			if (href) {
				const aTag = $('<a>',{ href: href });
				teaserBox.find("div.ce-textpic:first").wrap(aTag);
			}
		});
		
		$.fn.navikiStartPage = function() {
			$("#teaser").stop();
			
			if($( window ).width() > 991) {
				
				var margin = 20;
				var current = margin - $( document ).scrollTop();
				var lowerLimit = $( window ).height() - ($("#stage").outerHeight() + $("#stage").offset().top) + margin;
				var upperLimit = $( window ).height() - ($(".nivo-caption").first().outerHeight() + $(".nivo-caption").first().offset().top + $("#teaser").outerHeight());
	
				var bottom = current;
				if(current <= lowerLimit){
					bottom = lowerLimit;
				} else if(current >= upperLimit){
					bottom = upperLimit;
				}
				
				$("#teaser").animate({
				    bottom: bottom
				  },1200, function() {
				    // Animation complete.
				});
			
			} else {
				
				if ($("#teaser").css("bottom") != "") {
					$("#teaser").css("bottom","");
				}
				if ($("#teaser").css("position") != "") {
					$("#teaser").css("position","");
				}
			}
		};	
		
		var findCaption = window.setInterval(function(){
			
			if($(".nivo-caption")){
				
				if($(".nivo-caption").first().height() == null) {
					return;
				}
				
				if($(".nivo-caption").first().offset() == null){
					return;
				}

				$( ".nivo-caption" ).wrap( "<div class='container'></div>" );
				$( ".nivo-caption" ).wrap( "<div class='row'></div>" );
				$( ".nivo-caption" ).addClass("col-md-6 col-lg-3");

				$.fn.navikiStartPage();
				window.clearInterval(findCaption);
			
				$( ".nivo-caption" ).animate({
					opacity: 1.0
				  },2000, function() {
				    // Animation complete.
				});
				
				$( window ).resize(function() {
					$.fn.navikiStartPage();
				});
				
				$( window ).scroll(function () {
					$.fn.navikiStartPage();
				});
			}
		}, 1000);
		
		var findControlNav = window.setInterval(function(){
			if($(".nivo-controlNav").length > 0){
				$( ".nivo-controlNav" ).wrap( "<div class='container'></div>" );
				$( ".nivo-controlNav" ).wrap( "<div class='row'></div>" );
				$( ".nivo-controlNav" ).addClass("col-md-6 col-lg-3");
				$( ".nivo-controlNav" ).animate({
					opacity: 1.0
				  },2000, function() {
				    // Animation complete.
				});
				window.clearInterval(findControlNav);
			}
		}, 1000);	
	});
	
})( jQuery);
/**
 * Call function if user clicked on naviki logo. Changes the window location to
 * the start of the landingpage.
 * 
 */
;(function($) {
	
	$.fn.navikiFullPage = function() {
		
		/*
		 * Setup bootstrap column definitions for content elements (all other layouts)
		 */
		$('#fullpage .container:not(.layout-10) .ce-textpic.ce-intext.ce-right .ce-gallery').addClass('col-lg-6');
		$('#fullpage .container:not(.layout-10) .ce-textpic.ce-intext.ce-right .ce-bodytext').addClass('col-lg-5 col-lg-offset-1');
		$('#fullpage .container:not(.layout-10) .ce-textpic.ce-intext.ce-left .ce-gallery').addClass('col-lg-5 col-lg-offset-1');
		$('#fullpage .container:not(.layout-10) .ce-textpic.ce-intext.ce-left .ce-bodytext').addClass('col-lg-5');
		$('#fullpage .container:not(.layout-10) .ce-textpic.ce-intext.ce-right.ce-nowrap .ce-gallery').addClass('col-lg-6');
		$('#fullpage .container:not(.layout-10) .ce-textpic.ce-intext.ce-right.ce-nowrap .ce-bodytext').addClass('col-lg-5 col-lg-offset-1');
		$('#fullpage .container:not(.layout-10) .ce-textpic.ce-intext.ce-left.ce-nowrap .ce-gallery').addClass('col-lg-5 col-lg-offset-1');
		$('#fullpage .container:not(.layout-10) .ce-textpic.ce-intext.ce-left.ce-nowrap .ce-bodytext').addClass('col-lg-5');

		/* layout-10 (plain container) gets more dynamic grid boxes */
		$('#fullpage .container.layout-10 .ce-textpic.ce-intext.ce-right .ce-gallery').addClass('col-lg-6');
		$('#fullpage .container.layout-10 .ce-textpic.ce-intext.ce-right .ce-bodytext').addClass('col-lg col-lg-offset-1');
		$('#fullpage .container.layout-10 .ce-textpic.ce-intext.ce-left .ce-gallery').addClass('col-lg col-lg-offset-1');
		$('#fullpage .container.layout-10 .ce-textpic.ce-intext.ce-left .ce-bodytext').addClass('col-lg');
		$('#fullpage .container.layout-10 .ce-textpic.ce-intext.ce-right.ce-nowrap .ce-gallery').addClass('col-lg-6');
		$('#fullpage .container.layout-10 .ce-textpic.ce-intext.ce-right.ce-nowrap .ce-bodytext').addClass('col-lg col-lg-offset-1');
		$('#fullpage .container.layout-10 .ce-textpic.ce-intext.ce-left.ce-nowrap .ce-gallery').addClass('col-lg col-lg-offset-1');
		$('#fullpage .container.layout-10 .ce-textpic.ce-intext.ce-left.ce-nowrap .ce-bodytext').addClass('col-lg');
	
		/*
		 * Setup full-page classes, only first level of section items
		 */
		const sectionItems = $('#fullpage > .section');
		const length = sectionItems.length;
		sectionItems.each(function(index) {		
			var container = $(this).find(".container").first();
			
			if ($(this).hasClass("wrap")) {
				$(this).addClass("section-table");	
				container.wrap( "<div class='section-table-cell'></div>" );
			}
			
			if (index < length - 1) {
				var arrow = "<div class='arrow-down-container'><span data-next='"+(index+1)+"' class='icon-arrow-down-fullscreen'></span></div>";
				container.append(arrow);
			}
			
			if (index == 0) {
				if($(".fixedHeader").length > 0) {
					$(this).addClass("first-section");
				}
			}
		});

		// two-box-layout (layout-8)
		$("#fullpage .layout-8 h4").each(function( index ) {
			const header = $(this);
			const content = header.next();
			header.add(content).wrapAll( "<div class='two-box-layout' />");
		});
		$("#fullpage .layout-8 div.two-box-layout:nth-child(odd)").addClass("two-box-odd");
		$("#fullpage .layout-8 div.two-box-layout:nth-child(even)").addClass("two-box-even");
		$("#fullpage .layout-8 div.two-box-layout").last().after("<div class='two-box-layout-clear' />");
		$("#fullpage .layout-8 div.two-box-layout").wrapAll("<div class='two-box-layout-wrapper' />");

		/*
		 * Scroll effects
		 */
		$("#fullpage .icon-arrow-down-fullscreen").bind('click',function(event){
			const next = $(this).data("next");
			var destination = 0;

			// add additional top offset depending on page content
			const fixedHeaderHeight = $("#fixedHeader").length > 0 ? $("#fixedHeader").outerHeight() : 0;
			const fullPageContainerTop = $('#fullpage').position().top;
			const topOffset = fullPageContainerTop - fixedHeaderHeight;
			destination += topOffset;
			
			$('#fullpage .section').each(function(index) {
				if (index < next) {
					destination += $(this).outerHeight();
				}
			});
			
			if (typeof document.body.style.transitionProperty === 'string') {
			    // CSS3 transitions are supported. Yay!
				const avail = $(document).height() - $(window).height();
				var scroll = 0;

				if (avail > 0) {
					scroll = destination;
          
					if (scroll > avail) {
						scroll = avail;
					}
				} else {
					scroll = 0;
				}

				const deltaScroll = $(window).scrollTop() - scroll;

				// if we don't have to scroll because we're already at the right scrolling level,
				if (!deltaScroll) {
					return; // do nothing
				}
				
				event.preventDefault();

				const scrollContainer = $("#fullpage-scrolling").length > 0 ? $("#fullpage-scrolling") : $("#fullpage");

			    // translate3d forces hardware acceleration, second param is 'y'
			    const translate3d = 'translate3d(0, ' + deltaScroll + 'px, 0)';
				scrollContainer.css({
                	'transition' : ".7s",
                    '-webkit-transform': translate3d,
                    '-moz-transform': translate3d,
                    '-ms-transform':translate3d,
                    'transform': translate3d
                });
				$("#teaser").hide(); // if exists, hide teaser boxes while scrolling (start page)
			    
			    // revert back to native scrollTop
				scrollContainer.bind('transitionend webkitTransitionEnd msTransitionEnd oTransitionEnd', function(event) {
			    	$(document).scrollTop(destination);   	
			    	$(this).removeAttr("style");
			    	$(this).unbind(event);
					$("#teaser").show(); // if exists, show teaser boxes again (start page)
			    });
              
			} else {
				
				try {
					$(document).stop(true, true) // stop potential other jQuery animation (assuming we're the only one doing it)
					$(document).animate({
				        scrollTop: destination
				    }, 1000);
				} catch (e) {
					$(document).scrollTop(destination);   
				}
			}
		});
		
		$(window).on("scroll",function() {
			clearTimeout($.data(this, 'scrollTimer'));
		    $.data(this, 'scrollTimer', setTimeout(function() {
		    	var offset = 0;
				if ($(".fixedHeader").length > 0) {
					offset += $("#fixedHeader").outerHeight();
				}
				$("#fullpage .icon-arrow-down-fullscreen").each(function(index){
					if ($(window).scrollTop() + offset > $(this).offset().top) {
						$(this).css("visibility","hidden");
					} else {
						$(this).css("visibility","visible");
					}
				});
		    }, 250));
		    
			
		});
		
		
		/*
		 * Add break for four column layout.
		 * This break is visible under 1199px.
		 */
		$('#fullpage .ce-row').each(function(index) {
			var n = $(this).find(".ce-column" ).length;
			if (n == 4) {
				var e = $(this).find(".ce-column:eq( 1 )")
				$( "<div class='four-column-break'></div>" ).insertAfter(e);
			}
		});

		/*
		 * toggle the background color between green and white
		 */
		$('#fullpage >.section').each(function(index) {
			if (index % 2 == 0) {
				$(this).css("background-color","#EEEEEE");
			}
		});
		
		/*
		 * pick images from content element and set the image to the background of
		 * the fullpage section
		 */
		const bgImgsIdentifier = '#fullpage .section .csc-default.csc-img-bg img, '
								+ '#fullpage .section .csc-frame-default.csc-img-bg img, '
								+ '#fullpage .section .frame-default.csc-img-bg img';
		const bgImgs = $(bgImgsIdentifier);
		$.each(bgImgs, function(i, bgImg) {
			// get the image-url
			const imageUrl = 'url(' + bgImg.src + ')';
			const parentContainer = $(bgImg).parents('.section');
	
			// traverse along all parents and pick the section element
			parentContainer.css({
				'background-image' : imageUrl,
				'background-repeat': 'no-repeat'
			});
			
			// set full height for section with background image
			parentContainer.addClass("full-height");

			// hide the origin image
			$(bgImg).hide();
		});
		
		/*
		 * pick content element with layout-5 and set 
		 * vertical-align = top
		 */
		const ceIdentifier = '#fullpage .section .csc-default.layout-5, '
							+ '#fullpage .section .csc-frame-default.layout-5, '
							+ '#fullpage .section .frame-default.layout-5';
		const items = $(ceIdentifier);
		$.each(items, function(i, item) {
			// traverse along all parents and pick the section element
			$(item).parents('.section-table-cell').css({
				'vertical-align' : 'top'
			});
		});
		
		/*
		 * for single section, we set the height to full
		 */
		if ($('#fullpage .section').length == 1) {
			$('#fullpage .section').addClass("full-height");
		}

		// NAV-6801
		const calcBgImageHeight = function(element) {
			// get dimension of background image, remove url("")
			const bgImgUrl = element.css('background-image').replace(/url\("|"\)$/ig, "");
			if (bgImgUrl == "" || bgImgUrl == "none") {
				return;
			}

			const bgImg = new Image();
			bgImg.onload = function() {
				const xFactor = $(window).width() / bgImg.width;
				const bgImageHeight = bgImg.height * xFactor;
				// min-height with display:table not working in firefox
				element.css("height", bgImageHeight);
			}
			bgImg.src = bgImgUrl;
		}
		$(".full-height").each(function() {
			const element = $(this);
			calcBgImageHeight(element);
			$(window).resize(function() {calcBgImageHeight(element)});
		});
	};
	
	$.fn.navikiFullPage();
	
})( jQuery);