Repository: graphhopper/graphhopper
Pull request #819 (2016-10-03 15:42:13)

Add car4wd routing profile
Allow routing through tracks regardless of their tracktype value.
Including support for block_fords and block_barriers config settings.

karussell: Interesting. Would you mind to add some tests defining what should be different to a normal `car`?

Patch:
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index f7c4b0419..a51f9e3f9 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -36,7 +36,7 @@ After this you'll appear in the <a href="CONTRIBUTORS.md">contributors list</a>
 
 ## Code formatting
 
-We use IntelliJ defaults and a very similar configuration for NetBeans defined in the root pom.xml. Also for other IDEs 
+We use IntelliJ defaults and a very similar configuration for NetBeans defined in the root pom.xml. For eclipse there is this [configuration](https://github.com/graphhopper/graphhopper/files/481920/GraphHopper.Formatter.zip). Also for other IDEs 
 it should be simple to match:
 
  * Java indent is 4 spaces
diff --git a/.travis.yml b/.travis.yml
index e8040b6cc..37e4be176 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,12 +1,70 @@
 language: java
+sudo: false
+matrix:
+  fast_finish: true
+  include:
+      # Java 9 needs to be manually installed/upgraded
+      # see: https://github.com/travis-ci/travis-ci/issues/2968#issuecomment-149164058
+    - jdk: oraclejdk9
+      env: JVM=latest
+      sudo: required
+      dist: trusty
+      group: edge
+  allow_failures:
+    - jdk: oraclejdk9
+
+env:
+  global:
+    - BASEURL=https://www-eu.apache.org/dist/maven/maven-3/VERSION/binaries/apache-maven-VERSION-bin.zip
+    - FILE=apache-maven-VERSION-bin.zip
+    - DIR=apache-maven-VERSION
+    - VERSION=3.3.9
+    - secure: "CI_DEPLOY_USERNAME=central_username"
+    - secure: "CI_DEPLOY_PASSWORD=central_password"
+
 jdk:
   - oraclejdk8
-# do not install anything instead return true via unix command true
+
+before_install:
+   # update maven
+   - if [ "$TRAVIS_OS_NAME" == "linux" ]; then
+         wget --no-check-certificate $(echo -n $BASEURL | sed -e 's#VERSION#'$VERSION'#g');
+         unzip -qq $(echo -n $FILE | sed -e 's#VERSION#'$VERSION'#');
+         export M2_HOME=$PWD/$(echo -n $DIR | sed -e 's#VERSION#'$VERSION'#');
+         export PATH=$M2_HOME/bin:$PATH;
+     fi
+   # update java 9
+   - if [ "$TRAVIS_OS_NAME" == "linux" ] && [ "$JVM" == "latest" ]; then
+         sudo apt-get update -qq;
+         sudo /bin/echo -e oracle-java9-installer shared/accepted-oracle-license-v1-1 select true | sudo debconf-set-selections;
+         sudo apt-get install -y oracle-java9-installer;
+         sudo apt-get install -y oracle-java9-unlimited-jce-policy;
+         sudo update-java-alternatives -s java-9-oracle;
+     fi
+   - if [ "$TRAVIS_JDK_VERSION" == oraclejdk9 ]; then
+         sudo rm /etc/mavenrc;
+     fi
+
 install: true
-script: ./core/files/travis-build.sh
+
+script:
+  - mvn clean test verify -B
+  #- ./core/files/travis-build.sh
+
+after_success:
+  # deploy snapshot artifacts to maven central
+  - if [ "$TRAVIS_JDK_VERSION" == "oraclejdk8" ] &&
+       [ "$TRAVIS_BRANCH" == "master" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ]; then
+         travis_wait mvn deploy --settings core/files/settings.xml -DskipTests=true -B;
+    else
+        echo "Not deploying snapshot artifact for $TRAVIS_BRANCH";
+    fi
+
 notifications:
   email:
     - github@graphhopper.com
 
-# enable container-based stack
-sudo: false
+cache:
+  directories:
+  - $HOME/.m2
+
diff --git a/core/files/settings.xml b/core/files/settings.xml
new file mode 100644
index 000000000..80d2efb46
--- /dev/null
+++ b/core/files/settings.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
+  <servers>
+    <server>
+      <id>ossrh</id>
+      <username>${env.CI_DEPLOY_USERNAME}</username>
+      <password>${env.CI_DEPLOY_PASSWORD}</password>
+    </server>
+  </servers>
+</settings>
diff --git a/core/files/travis-build.sh b/core/files/travis-build.sh
index e0641fe52..60b1deacc 100755
--- a/core/files/travis-build.sh
+++ b/core/files/travis-build.sh
@@ -1,11 +1,9 @@
 HOME=$(dirname $0)
 cd $HOME/../..
 
-mvn clean test verify
-
 # npm tests disabled due to #632
 # cd $HOME/../../web
-# 
+#
 # sudo chown -R $USER ~/.npm
 # npm install
 # npm test && npm run lint
@@ -14,14 +12,14 @@ mvn clean test verify
 #for module in $modules; do
 #  echo "====== INSTALL $module ====="
 #  mvn -pl $module clean install -DskipTests=true
-#  EXIT_VAL="$?"    
+#  EXIT_VAL="$?"
 #  if [[ "x$EXIT_VAL" != "x0" ]]; then
 #    exit $EXIT_VAL
-#  fi 
-#  
+#  fi
+#
 #  echo "====== TEST $module ====="
 #  # verify necessary for failsafe, otherwise it won't fail the build!?
-#  mvn -pl $module test failsafe:integration-test verify  
+#  mvn -pl $module test failsafe:integration-test verify
 #  EXIT_VAL="$?"
 #  if [[ "x$EXIT_VAL" != "x0" ]]; then
 #    exit $EXIT_VAL
diff --git a/core/pom.xml b/core/pom.xml
index 220c5487e..4f00c252a 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -7,25 +7,25 @@
     <artifactId>graphhopper-core</artifactId>
     <name>GraphHopper Core</name>
     <version>0.8-SNAPSHOT</version>
-    <packaging>jar</packaging> 
+    <packaging>jar</packaging>
     <description>
-        GraphHopper is a fast and memory efficient Java road routing engine 
+        GraphHopper is a fast and memory efficient Java road routing engine
         working seamlessly with OpenStreetMap data.
     </description>
     <parent>
         <groupId>com.graphhopper</groupId>
-        <artifactId>graphhopper-parent</artifactId>    	
+        <artifactId>graphhopper-parent</artifactId>
         <version>0.8-SNAPSHOT</version>
     </parent>
-        
+
     <properties>
-        <netbeans.hint.license>apache20</netbeans.hint.license>        
+        <netbeans.hint.license>apache20</netbeans.hint.license>
         <!-- Make sure that we use the same format as for Helper.createFormatter.
              We cannot force the UTC TimeZone so it will just throw away the local offset or is this
              fixed due to https://issues.apache.org/jira/browse/MNG-5647 ?
         -->
-        <maven.build.timestamp.format>yyyy-MM-dd'T'HH:mm:ss'Z'</maven.build.timestamp.format>        
-        <builddate>${maven.build.timestamp}</builddate>        
+        <maven.build.timestamp.format>yyyy-MM-dd'T'HH:mm:ss'Z'</maven.build.timestamp.format>
+        <builddate>${maven.build.timestamp}</builddate>
     </properties>
     <licenses>
         <license>
@@ -34,20 +34,20 @@
             <distribution>repo</distribution>
             <comments>A business-friendly OSS license</comments>
         </license>
-    </licenses> 
+    </licenses>
     <dependencies>
         <dependency>
             <groupId>com.graphhopper</groupId>
             <artifactId>graphhopper-tools-lgpl</artifactId>
             <version>${project.parent.version}</version>
         </dependency>
-        
-        <!-- Trove is LGPL and slightly big (~3MB) -->        
+
+        <!-- Trove is LGPL and slightly big (~3MB) -->
         <dependency>
             <groupId>net.sf.trove4j</groupId>
             <artifactId>trove4j</artifactId>
             <version>3.0.3</version>
-        </dependency>        
+        </dependency>
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
@@ -61,7 +61,7 @@
             <scope>test</scope>
         </dependency>
         -->
-        
+
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-log4j12</artifactId>
@@ -74,13 +74,13 @@
             <version>${log4j.version}</version>
             <scope>test</scope>
         </dependency>
-        
+
         <!-- for using CGIAR: elevation data importing via tif files-->
         <dependency>
             <groupId>org.apache.xmlgraphics</groupId>
             <artifactId>xmlgraphics-commons</artifactId>
             <version>2.1</version>
-        </dependency>        
+        </dependency>
 
         <dependency>
             <groupId>org.json</groupId>
@@ -88,27 +88,29 @@
             <version>${json.org.version}</version>
             <scope>test</scope>
         </dependency>
-        
+
     </dependencies>
-        
+
     <build>
         <pluginManagement>
             <plugins>
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-assembly-plugin</artifactId>
-                    <configuration>                     
+                    <version>2.6</version>
+                    <configuration>
                         <!-- for usage on android -->
                         <descriptors>
                             <descriptor>src/main/assembly/android.xml</descriptor>
                         </descriptors>
                     </configuration>
-                </plugin>                
-                
-                <!-- create jar with test classes to be reused in other projects like external or our reader-osm -->                
+                </plugin>
+
+                <!-- create jar with test classes to be reused in other projects like external or our reader-osm -->
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-jar-plugin</artifactId>
+                    <version>3.0.2</version>
                     <executions>
                         <execution>
                             <goals>
@@ -117,10 +119,10 @@
                         </execution>
                     </executions>
                 </plugin>
-                
+
             </plugins>
         </pluginManagement>
-        
+
         <!-- make version available at runtime via version file -->
         <resources>
             <resource>
diff --git a/core/src/main/java/com/graphhopper/routing/AlgorithmOptions.java b/core/src/main/java/com/graphhopper/routing/AlgorithmOptions.java
index e206b35e9..964caa5eb 100644
--- a/core/src/main/java/com/graphhopper/routing/AlgorithmOptions.java
+++ b/core/src/main/java/com/graphhopper/routing/AlgorithmOptions.java
@@ -80,6 +80,9 @@ public class AlgorithmOptions {
             b.weighting(opts.getWeighting());
         if (opts.maxVisitedNodes >= 0)
             b.maxVisitedNodes(opts.maxVisitedNodes);
+        if (!opts.hints.isEmpty())
+            b.hints(opts.hints);
+
         return b;
     }
 
@@ -90,6 +93,10 @@ public class AlgorithmOptions {
         return traversalMode;
     }
 
+    public boolean hasWeighting() {
+        return weighting != null;
+    }
+
     public Weighting getWeighting() {
         assertNotNull(weighting, "weighting");
         return weighting;
@@ -140,7 +147,7 @@ public class AlgorithmOptions {
         public Builder algorithm(String algorithm) {
             this.opts.algorithm = algorithm;
             return this;
-        }        
+        }
 
         public Builder maxVisitedNodes(int maxVisitedNodes) {
             this.opts.maxVisitedNodes = maxVisitedNodes;
diff --git a/core/src/main/java/com/graphhopper/routing/util/Car4WDFlagEncoder.java b/core/src/main/java/com/graphhopper/routing/util/Car4WDFlagEncoder.java
new file mode 100644
index 000000000..8a6e468f9
--- /dev/null
+++ b/core/src/main/java/com/graphhopper/routing/util/Car4WDFlagEncoder.java
@@ -0,0 +1,109 @@
+/*
+ *  Licensed to GraphHopper GmbH under one or more contributor
+ *  license agreements. See the NOTICE file distributed with this work for 
+ *  additional information regarding copyright ownership.
+ * 
+ *  GraphHopper GmbH licenses this file to you under the Apache License, 
+ *  Version 2.0 (the "License"); you may not use this file except in 
+ *  compliance with the License. You may obtain a copy of the License at
+ * 
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package com.graphhopper.routing.util;
+
+import com.graphhopper.reader.ReaderWay;
+import com.graphhopper.routing.weighting.PriorityWeighting;
+import com.graphhopper.util.BitUtil;
+import com.graphhopper.util.EdgeIteratorState;
+import com.graphhopper.util.PMap;
+
+import java.util.HashSet;
+
+import static com.graphhopper.routing.util.PriorityCode.BEST;
+
+/**
+ * Defines bit layout for cars with four wheel drive
+ * <p>
+ *
+ * @author Peter Karich
+ * @author boldtrn
+ * @author zstadler
+ */
+public class Car4WDFlagEncoder extends CarFlagEncoder {
+
+    public Car4WDFlagEncoder() {
+        this(5, 5, 0);
+    }
+
+    public Car4WDFlagEncoder(PMap properties) {
+	super(properties);
+    }
+
+    public Car4WDFlagEncoder(String propertiesStr) {
+        this(new PMap(propertiesStr));
+    }
+
+    public Car4WDFlagEncoder(int speedBits, double speedFactor, int maxTurnCosts) {
+        super(speedBits, speedFactor, maxTurnCosts);
+
+        init();
+    }
+
+    @Override
+    public int getVersion() {
+        return 1;
+    }
+
+    @Override
+    public long acceptWay(ReaderWay way) {
+        // TODO: Ferries have conditionals, like opening hours or are closed during some time in the year
+        String highwayValue = way.getTag("highway");
+        if (highwayValue == null) {
+            if (way.hasTag("route", ferries)) {
+                String motorcarTag = way.getTag("motorcar");
+                if (motorcarTag == null)
+                    motorcarTag = way.getTag("motor_vehicle");
+
+                if (motorcarTag == null && !way.hasTag("foot") && !way.hasTag("bicycle") || "yes".equals(motorcarTag))
+                    return acceptBit | ferryBit;
+            }
+            return 0;
+        }
+
+        if (!defaultSpeedMap.containsKey(highwayValue))
+            return 0;
+
+        if (way.hasTag("impassable", "yes") || way.hasTag("status", "impassable"))
+            return 0;
+
+        // multiple restrictions needs special handling compared to foot and bike, see also motorcycle
+        String firstValue = way.getFirstPriorityTag(restrictions);
+        if (!firstValue.isEmpty()) {
+            if (restrictedValues.contains(firstValue) && !getConditionalTagInspector().isRestrictedWayConditionallyPermitted(way))
+                return 0;
+            if (intendedValues.contains(firstValue))
+                return acceptBit;
+        }
+
+        // do not drive street cars into fords
+        if (isBlockFords() && ("ford".equals(highwayValue) || way.hasTag("ford")))
+            return 0;
+
+        if (getConditionalTagInspector().isPermittedWayConditionallyRestricted(way))
+            return 0;
+        else
+            return acceptBit;
+    }
+
+
+    @Override
+    public String toString() {
+        return "car4wd";
+    }
+}
diff --git a/core/src/main/java/com/graphhopper/routing/util/DefaultFlagEncoderFactory.java b/core/src/main/java/com/graphhopper/routing/util/DefaultFlagEncoderFactory.java
index 3455ea07d..4a8dc89a7 100644
--- a/core/src/main/java/com/graphhopper/routing/util/DefaultFlagEncoderFactory.java
+++ b/core/src/main/java/com/graphhopper/routing/util/DefaultFlagEncoderFactory.java
@@ -33,6 +33,9 @@ public class DefaultFlagEncoderFactory implements FlagEncoderFactory {
         else if (name.equals(CAR))
             return new CarFlagEncoder(configuration);
 
+        else if (name.equals(CAR4WD))
+            return new Car4WDFlagEncoder(configuration);
+
         if (name.equals(BIKE))
             return new BikeFlagEncoder(configuration);
 
diff --git a/core/src/main/java/com/graphhopper/routing/util/FlagEncoderFactory.java b/core/src/main/java/com/graphhopper/routing/util/FlagEncoderFactory.java
index feee82647..82e8b45d7 100644
--- a/core/src/main/java/com/graphhopper/routing/util/FlagEncoderFactory.java
+++ b/core/src/main/java/com/graphhopper/routing/util/FlagEncoderFactory.java
@@ -24,6 +24,7 @@ import com.graphhopper.util.PMap;
  */
 public interface FlagEncoderFactory {
     final String CAR = "car";
+    final String CAR4WD = "car4wd";
     final String BIKE = "bike";
     final String BIKE2 = "bike2";
     final String RACINGBIKE = "racingbike";
diff --git a/core/src/main/java/com/graphhopper/util/PMap.java b/core/src/main/java/com/graphhopper/util/PMap.java
index e6edcbc0a..24d4d7141 100644
--- a/core/src/main/java/com/graphhopper/util/PMap.java
+++ b/core/src/main/java/com/graphhopper/util/PMap.java
@@ -170,6 +170,10 @@ public class PMap {
         return this;
     }
 
+    public boolean isEmpty() {
+        return map.isEmpty();
+    }
+
     @Override
     public String toString() {
         return getMap().toString();
diff --git a/graphhopper.sh b/graphhopper.sh
index ef756718e..7d50b8fae 100755
--- a/graphhopper.sh
+++ b/graphhopper.sh
@@ -1,4 +1,6 @@
 #!/bin/bash
+(set -o igncr) 2>/dev/null && set -o igncr; # this comment is required for handling Windows cr/lf 
+# See StackOverflow answer http://stackoverflow.com/a/14607651
 
 GH_CLASS=com.graphhopper.tools.Import
 GH_HOME=$(dirname "$0")
diff --git a/pom.xml b/pom.xml
index fad7af625..a5f5cb1f2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,8 +7,8 @@
     <artifactId>graphhopper-parent</artifactId>
     <name>GraphHopper Parent Project</name>
     <version>0.8-SNAPSHOT</version>
-    <packaging>pom</packaging> 
-    <url>https://graphhopper.com</url> 
+    <packaging>pom</packaging>
+    <url>https://graphhopper.com</url>
     <inceptionYear>2012</inceptionYear>
     <description>Super pom of GraphHopper, the fast and flexible routing engine</description>
 
@@ -17,7 +17,7 @@
         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
         <slf4j.version>1.7.21</slf4j.version>
         <log4j.version>1.2.17</log4j.version>
-        <json.org.version>20160212</json.org.version>                
+        <json.org.version>20160212</json.org.version>
 
         <org-netbeans-modules-editor-indent.CodeStyle.project.indent-shift-width>4</org-netbeans-modules-editor-indent.CodeStyle.project.indent-shift-width>
         <org-netbeans-modules-editor-indent.CodeStyle.project.spaces-per-tab>4</org-netbeans-modules-editor-indent.CodeStyle.project.spaces-per-tab>
@@ -35,20 +35,20 @@
         <org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignMultilineFor>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignMultilineFor>
         <org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignMultilineTryResources>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignMultilineTryResources>
     </properties>
-    
+
     <scm>
         <connection>scm:git:git@github.com:graphhopper/graphhopper.git</connection>
         <developerConnection>scm:git:git@github.com:graphhopper/graphhopper.git</developerConnection>
         <url>git@github.com:graphhopper/graphhopper.git</url>
     </scm>
-    
+
     <licenses>
         <license>
             <name>Apache License, Version 2.0</name>
             <url>http://www.apache.org/licenses/LICENSE-2.0</url>
         </license>
     </licenses>
-    
+
     <developers>
         <developer>
             <id>karussell</id>
@@ -56,7 +56,7 @@
             <email>my.name@graphhopper.com</email>
         </developer>
     </developers>
-    
+
     <mailingLists>
         <mailingList>
             <name>GraphHopper</name>
@@ -76,19 +76,21 @@
         <module>tools</module>
         <module>web</module>
     </modules>
-        
-    <build>        
-        <plugins>       
+    <prerequisites>
+        <maven>3.2</maven>
+    </prerequisites>
+    <build>
+        <plugins>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
                 <version>3.5.1</version>
                 <configuration>
                     <!--
-                    <compilerArgument>-Xlint:unchecked</compilerArgument>                    
+                    <compilerArgument>-Xlint:unchecked</compilerArgument>
                     <compilerArgument>-Xlint:deprecation</compilerArgument>
                     -->
-                    
+
                     <!-- suppress warning about Unsafe functionality -->
                     <compilerArgument>-XDignore.symbol.file</compilerArgument>
                     <fork>true</fork>
@@ -96,9 +98,9 @@
                     <target>1.7</target>
                 </configuration>
             </plugin>
-            
+
             <!-- to run single tests -->
-            <plugin>                
+            <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-surefire-plugin</artifactId>
                 <version>2.19.1</version>
@@ -106,11 +108,11 @@
                     <argLine>-Xmx100m -Xms100m</argLine>
                 </configuration>
             </plugin>
-                        
+
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-failsafe-plugin</artifactId>
-                <version>2.19</version>
+                <version>2.19.1</version>
                 <executions>
                     <execution>
                         <goals>
@@ -120,21 +122,21 @@
                     </execution>
                 </executions>
             </plugin>
-                        
+
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-assembly-plugin</artifactId>
-                <version>2.6</version>                    
+                <version>2.6</version>
             </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-jar-plugin</artifactId>
-                <version>2.6</version>
+                <version>3.0.2</version>
             </plugin>
             <plugin>
                 <artifactId>maven-war-plugin</artifactId>
-                <version>2.6</version>                
-            </plugin>            
+                <version>3.0.0</version>
+            </plugin>
             <!-- example https://github.com/tananaev/traccar/blob/master/checkstyle.xml
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
@@ -145,26 +147,26 @@
                 </configuration>
             </plugin>
             -->
-            
+
             <!-- create html output for findbugs -->
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-site-plugin</artifactId>
-                <version>3.5</version>
+                <version>3.5.1</version>
                 <configuration>
                     <reportPlugins>
                         <plugin>
                             <groupId>org.codehaus.mojo</groupId>
-                            <artifactId>findbugs-maven-plugin</artifactId>                            
+                            <artifactId>findbugs-maven-plugin</artifactId>
                         </plugin>
                     </reportPlugins>
                 </configuration>
             </plugin>
-            
+
             <plugin>
                 <groupId>org.codehaus.mojo</groupId>
                 <artifactId>findbugs-maven-plugin</artifactId>
-                <version>3.0.3</version>                
+                <version>3.0.4</version>
             </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
@@ -177,24 +179,24 @@
                 -->
             </plugin>
         </plugins>
-    </build>    
-    
+    </build>
+
     <dependencies>
         <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
             <version>4.12</version>
             <scope>test</scope>
-        </dependency>        
+        </dependency>
     </dependencies>
-    
+
     <distributionManagement>
         <snapshotRepository>
             <id>ossrh</id>
             <url>https://oss.sonatype.org/content/repositories/snapshots</url>
         </snapshotRepository>
-    </distributionManagement> 
-    
+    </distributionManagement>
+
     <!-- mvn clean deploy -P release -->
     <profiles>
         <profile>
@@ -229,11 +231,11 @@
                             <autoReleaseAfterClose>true</autoReleaseAfterClose>
                         </configuration>
                     </plugin>
-            
+
                     <plugin>
                         <groupId>org.apache.maven.plugins</groupId>
                         <artifactId>maven-javadoc-plugin</artifactId>
-                        <version>2.10.3</version>
+                        <version>2.10.4</version>
                         <executions>
                             <execution>
                                 <id>attach-javadocs</id>
@@ -246,7 +248,7 @@
                     <plugin>
                         <groupId>org.apache.maven.plugins</groupId>
                         <artifactId>maven-source-plugin</artifactId>
-                        <version>3.0.0</version>
+                        <version>3.0.1</version>
                         <executions>
                             <execution>
                                 <id>attach-sources</id>
@@ -259,7 +261,7 @@
                 </plugins>
             </build>
         </profile>
-        
+
         <profile>
             <id>include-android</id>
             <activation>
@@ -270,5 +272,5 @@
             </modules>
         </profile>
     </profiles>
-    
+
 </project>
diff --git a/web/package.json b/web/package.json
index 53bf99da0..8502e72b8 100644
--- a/web/package.json
+++ b/web/package.json
@@ -25,14 +25,14 @@
     "browserify": "13.1.0",
     "browserify-swap": "0.2.2",
     "d3": "3.5.17",
-    "jquery": "3.1.0",
+    "jquery": "3.1.1",
     "leaflet": "1.0.1",
-    "leaflet-loading": "0.1.22",
-    "uglifyify": "3.0.2"
+    "leaflet-loading": "0.1.23",
+    "uglifyify": "3.0.3"
   },
   "devDependencies": {
-    "jasmine": "2.4.1",
-    "jshint": "2.9.2",
+    "jasmine": "2.5.2",
+    "jshint": "2.9.3",
     "watchify": "3.7.0"
   }
 }
diff --git a/web/pom.xml b/web/pom.xml
index fea85a31a..334f97693 100644
--- a/web/pom.xml
+++ b/web/pom.xml
@@ -9,35 +9,35 @@
     <version>0.8-SNAPSHOT</version>
     <name>GraphHopper Web</name>
     <description>Example on how to use GraphHopper in a web-based application</description>
-        
+
     <parent>
         <groupId>com.graphhopper</groupId>
-        <artifactId>graphhopper-parent</artifactId>    	
+        <artifactId>graphhopper-parent</artifactId>
         <version>0.8-SNAPSHOT</version>
     </parent>
     <properties>
         <jetty.version>9.3.8.v20160314</jetty.version>
     </properties>
-    
+
     <dependencies>
         <dependency>
             <groupId>com.graphhopper</groupId>
             <artifactId>graphhopper-reader-osm</artifactId>
-            <version>${project.parent.version}</version>            
+            <version>${project.parent.version}</version>
         </dependency>
-        
+
         <dependency>
             <groupId>org.json</groupId>
-            <artifactId>json</artifactId>            
+            <artifactId>json</artifactId>
             <version>${json.org.version}</version>
-        </dependency>    
-        
+        </dependency>
+
         <dependency>
             <groupId>com.google.inject</groupId>
             <artifactId>guice</artifactId>
             <version>4.0</version>
         </dependency>
-        
+
         <!-- necessary to use guice ('@Inject') in servlets -->
         <dependency>
             <groupId>com.google.inject.extensions</groupId>
@@ -56,7 +56,7 @@
             <version>${slf4j.version}</version>
             <scope>runtime</scope>
         </dependency>
-        
+
         <dependency>
             <groupId>log4j</groupId>
             <artifactId>log4j</artifactId>
@@ -69,7 +69,7 @@
             <artifactId>jetty-servlets</artifactId>
             <version>${jetty.version}</version>
         </dependency>
-        
+
         <dependency>
             <groupId>org.eclipse.jetty</groupId>
             <artifactId>jetty-server</artifactId>
@@ -80,7 +80,7 @@
             <artifactId>jetty-servlet</artifactId>
             <version>${jetty.version}</version>
         </dependency>
-        
+
         <!-- for integration tests of service -->
         <dependency>
             <groupId>org.eclipse.jetty</groupId>
@@ -88,14 +88,15 @@
             <version>${jetty.version}</version>
             <scope>test</scope>
         </dependency>
-      
+
     </dependencies>
 
     <build>
         <plugins>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-compiler-plugin</artifactId>                
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.5.1</version>
                 <configuration>
                     <source>1.8</source>
                     <target>1.8</target>
@@ -104,14 +105,25 @@
             <!-- create a jar file too, so others can use it more easily -->
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-war-plugin</artifactId>                
+                <artifactId>maven-war-plugin</artifactId>
+                <version>3.0.0</version>
                 <configuration>
                     <attachClasses>true</attachClasses>
                 </configuration>
-            </plugin>            
+            </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-assembly-plugin</artifactId>
+                <!-- this is an old version but 2.6 fails with java 9
+                see: https://stackoverflow.com/questions/36583118/is-maven-ready-for-jdk9-->
+                <version>2.4.1</version>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.codehaus.plexus</groupId>
+                        <artifactId>plexus-archiver</artifactId>
+                        <version>2.4.4</version>
+                    </dependency>
+                </dependencies>
                 <configuration>
                     <archive>
                         <manifest>
@@ -128,14 +140,14 @@
                     <execution>
                         <id>make-assembly</id>
                         <!-- bind to verify and not package to pass integration tests before creating assemblies -->
-                        <phase>integration-test</phase> 
+                        <phase>integration-test</phase>
                         <goals>
                             <goal>single</goal>
                         </goals>
                     </execution>
                 </executions>
             </plugin>
- 
+
         </plugins>
     </build>
 
diff --git a/web/src/main/webapp/css/leaflet.contextmenu.css b/web/src/main/webapp/css/leaflet.contextmenu.css
index cd83d4b2a..0b5e2defc 100644
--- a/web/src/main/webapp/css/leaflet.contextmenu.css
+++ b/web/src/main/webapp/css/leaflet.contextmenu.css
@@ -28,10 +28,6 @@
     opacity: 0.5;
 }
 
-.leaflet-contextmenu a.leaflet-contextmenu-item-hidden {
-    display: none;
-}
-
 .leaflet-contextmenu a.leaflet-contextmenu-item.over {
     background-color: #f4f4f4;
     border-top: 1px solid #f0f0f0;
@@ -56,7 +52,3 @@
     border-bottom: 1px solid #ccc;
     margin: 5px 0;
 }
-
-.leaflet-contextmenu-separator-hidden {
-    display: none;
-}
\ No newline at end of file
diff --git a/web/src/main/webapp/css/style.css b/web/src/main/webapp/css/style.css
index cdfd5e9f5..988f3f109 100644
--- a/web/src/main/webapp/css/style.css
+++ b/web/src/main/webapp/css/style.css
@@ -18,7 +18,7 @@ body {
     /*padding-right: 15px; */
 }
 
-#info {    
+#info {
     margin-top: 10px;
     display: none;
     padding: 5px;
@@ -28,7 +28,7 @@ body {
     padding-right: 5px;
 }
 
-.route_results {    
+.route_results {
     max-height: 40%;
 }
 
@@ -221,7 +221,7 @@ tr.instruction {
     border-bottom: #dadada dashed thin;
 }
 
-.instructions {    
+.instructions {
     table-layout:fixed;
     border-collapse: collapse;
     padding-top: 10px;
@@ -321,7 +321,7 @@ td img.pic {
 .pointDelete:hover, .pointAdd:hover { cursor: pointer; }
 .pointDelete:disabled {
     background: red;
-} 
+}
 
 .expandDetails {
     color: gray;
@@ -339,13 +339,18 @@ td img.pic {
     background-image: linear-gradient(to bottom, white, #e7e7e7);
 }
 
-.ui-dialog > .ui-widget-header{
-    padding:1em;    
+.ui-dialog {
+  /* Must be > 700, the z-index of .leaflet-popup-pane */
+  z-index: 1000;
+}
+
+.ui-dialog > .ui-widget-header {
+    padding: 1em;
     border: 1px solid black;
 }
 
 .ui-dialog .ui-dialog-content {
-    padding:1em
+    padding: 1em;
 }
 
 .ui-dialog .ui-widget-content {
@@ -367,8 +372,8 @@ td img.pic {
 }
 
 #route_result_tabs {
-    list-style: none; 
-    padding: 0; 
+    list-style: none;
+    padding: 0;
     margin: 0;
 }
 
@@ -392,7 +397,7 @@ td img.pic {
 }
 
 .route_result_tab.current {
-    display: block;    
+    display: block;
 }
 
 #donate_form {
diff --git a/web/src/main/webapp/js/lib/leaflet.contextmenu.js b/web/src/main/webapp/js/lib/leaflet.contextmenu.js
index 4cae713af..f10e76865 100644
--- a/web/src/main/webapp/js/lib/leaflet.contextmenu.js
+++ b/web/src/main/webapp/js/lib/leaflet.contextmenu.js
@@ -1,618 +1,565 @@
 /*
-Leaflet.contextmenu, a context menu for Leaflet.
-(c) 2014, Adam Ratcliffe, GeoSmart Maps Limited
-contribute 2014, Roland Braband, NRC
- */
+	Leaflet.contextmenu, a context menu for Leaflet.
+	(c) 2015, Adam Ratcliffe, GeoSmart Maps Limited
+       
+        @preserve
+*/
+
+(function(factory) {
+	// Packaging/modules magic dance
+	var L;
+	if (typeof define === 'function' && define.amd) {
+		// AMD
+		define(['leaflet'], factory);
+	} else if (typeof module !== 'undefined') {
+		// Node/CommonJS
+		L = require('leaflet');
+		module.exports = factory(L);
+	} else {
+		// Browser globals
+		if (typeof window.L === 'undefined') {
+			throw new Error('Leaflet must be loaded first');
+		}
+		factory(window.L);
+	}
+})(function(L) {
 L.Map.mergeOptions({
-    contextmenuItems : []
+	contextmenuItems: []
 });
 
 L.Map.ContextMenu = L.Handler.extend({
 
-        statics : {
-            BASE_CLS : 'leaflet-contextmenu'
-        },
-
-        initialize : function (map) {
-            L.Handler.prototype.initialize.call(this, map);
-
-            this._items = [];
-            this._sets = [];
-            this._state = 0;
-            this._defaultState = map.options.contextmenuDefaultState || 1;
-            this._activeState = map.options.contextmenuAtiveState || 1;
-            this._visible = false;
-
-            var container = this._container = L.DomUtil.create('div', L.Map.ContextMenu.BASE_CLS, map._container);
-            container.style.zIndex = 1e4;
-            container.style.position = 'absolute';
-
-            if (map.options.contextmenuWidth) {
-                container.style.width = map.options.contextmenuWidth + 'px';
-            }
-            if (map.options.contextmenuSets === undefined || map.options.contextmenuSets.length === 0) {
-                map.options.contextmenuSets = [{
-                        name : 'set_default',
-                        state : this._defaultState
-                    }
-                ];
-            }
-
-            this._createItems();
-            this._createSets();
-            this._changeState();
-            L.DomEvent
-            .on(container, 'click', L.DomEvent.stop)
-            .on(container, 'mousedown', L.DomEvent.stop)
-            .on(container, 'dblclick', L.DomEvent.stop)
-            .on(container, 'contextmenu', L.DomEvent.stop);
-        },
-
-        addHooks : function () {
-            L.DomEvent
-            .on(document, ((L.Browser.touch) ? 'touchstart' : 'mousedown'), this._onMouseDown, this)
-            .on(document, 'keydown', this._onKeyDown, this);
-
-            this._map.on({
-                contextmenu : this._show,
-                mouseout : this._hide,
-                mousedown : this._hide,
-                movestart : this._hide,
-                zoomstart : this._hide
-            }, this);
-        },
-
-        removeHooks : function () {
-            L.DomEvent.off(document, 'keydown', this._onKeyDown, this);
-
-            this._map.off({
-                contextmenu : this._show,
-                mouseout : this._hide,
-                mousedown : this._hide,
-                movestart : this._hide,
-                zoomstart : this._hide
-            }, this);
-
-        },
-
-        showAt : function (point, data, state) {
-            if (point instanceof L.LatLng) {
-                point = this._map.latLngToContainerPoint(point);
-            }
-            this._showAtPoint(point, data, state);
-        },
-
-        hide : function () {
-            this._hide();
-        },
-
-        setState : function (state) {
-            return this._changeState(state);
-        },
-
-        setActiveState : function (state) {
-            var set,
-            state = (state !== undefined) ? state : this._activeState,
-            el,
-            i,
-            l;
-
-            for (i = 0, l = this._sets.length; i < l; i++) {
-                set = this._sets[i];
-                if (set.state === state) {
-                    this._activeState = state;
-                    break;
-                }
-            }
-            return set;
-        },
-
-        getState : function () {
-            return this._state;
-        },
-
-        addSet : function (options) {
-            return this.insertSet(options);
-        },
-
-        insertSet : function (options, id) {
-            var id = (id !== undefined) ? id : this._sets.length,
-            set = this._createSet(options, id);
-
-            this._sets.push(set);
-            return set;
-        },
-
-        addItem : function (options) {
-            return this.insertItem(options);
-        },
-
-        insertItem : function (options, index) {
-            var index = (index !== undefined) ? index : this._items.length,
-            item = this._createItem(this._container, options, index);
-
-            this._items.push(item);
-            this._sizeChanged = true;
-            this._map.fire('contextmenu.additem', {
-                contextmenu : this,
-                el : item.el,
-                index : index
-            });
-
-            return item.el;
-        },
-
-        removeItem : function (item) {
-            var container = this._container;
-
-            if (!isNaN(item)) {
-                item = container.children[item];
-            }
-
-            if (item !== undefined) {
-                this._removeItem(L.Util.stamp(item));
-
-                this._sizeChanged = true;
-
-                this._map.fire('contextmenu.removeitem', {
-                    contextmenu : this,
-                    el : item
-                });
-
-            }
-        },
-
-        removeAllItems : function () {
-            var item;
-            
-            while (this._container.children.length) {
-                item = this._container.children[0];
-                this._removeItem(L.Util.stamp(item));
-            }
-        },
-
-        setDisabled : function (item, disabled) {
-            var container = this._container,
-            itemCls = L.Map.ContextMenu.BASE_CLS + '-item';
-
-            if (!isNaN(item)) {
-                item = container.children[item];
-            }
-
-            if (item !== undefined && L.DomUtil.hasClass(item, itemCls)) {
-                if (disabled) {
-                    L.DomUtil.addClass(item, itemCls + '-disabled');
-                    this._map.fire('contextmenu.disableitem', {
-                        contextmenu : this,
-                        el : item
-                    });
-                } else {
-                    L.DomUtil.removeClass(item, itemCls + '-disabled');
-                    this._map.fire('contextmenu.enableitem', {
-                        contextmenu : this,
-                        el : item
-                    });
-                }
-            }
-        },
-
-        setHidden : function (item, hidden) {
-            var container = this._container,
-            itemCls = L.Map.ContextMenu.BASE_CLS + '-item',
-            separatorCls = L.Map.ContextMenu.BASE_CLS + '-separator';
-
-            if (!isNaN(item)) {
-                item = container.children[item];
-            }
-            if (item !== undefined && L.DomUtil.hasClass(item, itemCls)) {
-                if (hidden) {
-                    L.DomUtil.addClass(item, itemCls + '-hidden');
-                    this._map.fire('contextmenu.hideitem', {
-                        contextmenu : this,
-                        el : item
-                    });
-
-                } else {
-                    L.DomUtil.removeClass(item, itemCls + '-hidden');
-                    this._map.fire('contextmenu.showitem', {
-                        contextmenu : this,
-                        el : item
-                    });
-                }
-            } else if (item !== undefined && L.DomUtil.hasClass(item, separatorCls)) {
-                if (hidden) {
-                    L.DomUtil.addClass(item, separatorCls + '-hidden');
-                } else {
-                    L.DomUtil.removeClass(item, separatorCls + '-hidden');
-                }
-            }
-        },
-
-        isVisible : function () {
-            return this._visible;
-        },
-
-        // private methods
-        _changeState : function (state) {
-            var set,
-            state = (state !== undefined) ? state : this._defaultState,
-            item,
-            el,
-            i,
-            l;
-            
-            if (state !== this._state) {
-                for (i = 0, l = this._sets.length; i < l; i++) {
-                    set = this._sets[i];
-                    if (set.state === state || (set.name === state && set.state !== this._state)) {
-                        this._map.fire('contextmenu.changestate', {
-                            contextmenu : this,
-                            set : set,
-                            state : state
-                        });
-                        for (i = 0, l = this._items.length; i < l; i++) {
-                            item = this._items[i];
-                            this.setHidden(this._items[i].el, (item.state.indexOf(set.state) === -1 && item.state.indexOf(set.name) === -1));
-                        }
-                        this._sizeChanged = true;
-                        this._state = state;
-                        break;
-                    }
-                }
-            }
-            return set;
-        },
-
-        _createSets : function () {
-            var setOptions = this._map.options.contextmenuSets,
-            set,
-            i,
-            l;
-
-            for (i = 0, l = setOptions.length; i < l; i++) {
-                this._sets.push(this._createSet(setOptions[i], this._sets.length));
-            }
-        },
-
-        _createSet : function (options, id) {
-            var name = (options.name !== undefined) ? options.name : 'set_' + id;
-            return {
-                id : id,
-                name : options.name,
-                state : options.state
-            };
-        },
-
-        _createItems : function () {
-            var itemOptions = this._map.options.contextmenuItems,
-            item,
-            i,
-            l;
-
-            for (i = 0, l = itemOptions.length; i < l; i++) {
-                this._items.push(this._createItem(this._container, itemOptions[i]));
-            }
-        },
-
-        _createItem : function (container, options, index) {
-            if (options.separator || options === '-') {
-                return this._createSeparator(container, index, options.state);
-            }
-
-            var itemCls = L.Map.ContextMenu.BASE_CLS + '-item',
-            state = (options.state !== undefined) ? ((Array.isArray(options.state)) ? options.state : [options.state]) : [this._defaultState],
-            cls = (options.disabled) ? (itemCls + ' ' + itemCls + '-disabled') : ((options.hidden) ? (itemCls + ' ' + itemCls + '-hidden') : itemCls),
-            el = this._insertElementAt('a', cls, container, index),
-            callback = this._createEventHandler(el, options.callback, options.context, options.hideOnSelect),
-            html = '';
-
-            if (options.icon) {
-                html = '<img class="' + L.Map.ContextMenu.BASE_CLS + '-icon" src="' + options.icon + '"/>';
-            } else if (options.iconCls) {
-                html = '<span class="' + L.Map.ContextMenu.BASE_CLS + '- icon ' + options.iconCls + '"></span>';
-            }
-
-            el.innerHTML = html + options.text;
-            el.href = '#';
-            L.DomEvent
-            .on(el, 'mouseover', this._onItemMouseOver, this)
-            .on(el, 'mouseout', this._onItemMouseOut, this)
-            .on(el, 'mousedown', L.DomEvent.stopPropagation)
-            .on(el, 'click', callback);
-
-            return {
-                id : L.Util.stamp(el),
-                el : el,
-                callback : callback,
-                state : state
-            };
-        },
-
-        _removeItem : function (id) {
-            var item,
-            callback,
-            el,
-            i,
-            l;
-
-            for (i = 0, l = this._items.length; i < l; i++) {
-                item = this._items[i];
-
-                if (item.id === id) {
-                    el = item.el;
-                    callback = item.callback;
-
-                    if (callback) {
-                        L.DomEvent
-                        .off(el, 'mouseover', this._onItemMouseOver, this)
-                        .off(el, 'mouseover', this._onItemMouseOut, this)
-                        .off(el, 'mousedown', L.DomEvent.stopPropagation)
-                        .off(el, 'click', item.callback);
-
-                    }
+	_touchstart: L.Browser.msPointer ? 'MSPointerDown' : L.Browser.pointer ? 'pointerdown' : 'touchstart',
 
-                    this._container.removeChild(el);
-                    this._items.splice(i, 1);
-                    return item;
-
-                }
-            }
-            return null;
-        },
-
-        _createSeparator : function (container, index, state) {
-            var el = this._insertElementAt('div', L.Map.ContextMenu.BASE_CLS + '-separator', container, index),
-            state = (state !== undefined) ? ((Array.isArray(state)) ? state : [state]) : [this._defaultState];
-
-            return {
-                id : L.Util.stamp(el),
-                el : el,
-                state : state
-            };
-        },
-
-        _createEventHandler : function (el, func, context, hideOnSelect) {
-            var me = this,
-            map = this._map,
-            disabledCls = L.Map.ContextMenu.BASE_CLS + '-item-disabled',
-            hideOnSelect = (hideOnSelect !== undefined) ? hideOnSelect : true;
-
-            return function (e) {
-                if (L.DomUtil.hasClass(el, disabledCls)) {
-                    return;
-
-                }
-
-                if (hideOnSelect) {
-                    me._hide();
-
-                }
-
-                if (func) {
-                    func.call(context || map, me._showLocation);
-                }
-
-                me._map.fire('contextmenu:select', {
-                    contextmenu : me,
-                    el : el
-                });
-            };
-        },
-
-        _insertElementAt : function (tagName, className, container, index) {
-            var refEl,
-            el = document.createElement(tagName);
-
-            el.className = className;
-
-            if (index !== undefined) {
-                refEl = container.children[index];
-            }
-
-            if (refEl) {
-                container.insertBefore(el, refEl);
-            } else {
-                container.appendChild(el);
-            }
-
-            return el;
-        },
-
-        _show : function (e) {
-            this._showAtPoint(e.containerPoint);
-        },
-
-        _showAtPoint : function (pt, data, state) {
-            if (this._items.length) {
-                var map = this._map,
-                layerPoint = map.containerPointToLayerPoint(pt),
-                latlng = map.layerPointToLatLng(layerPoint),
-                event = {
-                    contextmenu : this,
-                    state : state
-                },
-                state = (state !== undefined) ? state : this._activeState;
-
-                if (data) {
-                    event = L.extend(data, event);
-                }
-
-                this._showLocation = {
-                    state : state,
-                    target : (data) ? data.relatedTarget : null,
-                    latlng : latlng,
-                    layerPoint : layerPoint,
-                    containerPoint : pt
-                };
-
-                this._setPosition(pt);
-                this._changeState(state);
-                
-                if (!this._visible) {
-                    this._container.style.display = 'block';
-                    this._visible = true;
-                } else {
-                    this._setPosition(pt);
-                }
-                
-
-                this._map.fire('contextmenu.show', event);
-            }
-        },
-
-        _hide : function () {
-            if (this._visible) {
-                this.setState(this._defaultState);
-                this._visible = false;
-                this._container.style.display = 'none';
-                this._map.fire('contextmenu.hide', {
-                    contextmenu : this
-                });
-            }
-        },
-
-        _setPosition : function (pt) {
-            var mapSize = this._map.getSize(),
-            container = this._container,
-            containerSize = this._getElementSize(container),
-            anchor;
-
-            if (this._map.options.contextmenuAnchor) {
-                anchor = L.point(this._map.options.contextmenuAnchor);
-                pt = pt.add(anchor);
-            }
-
-            container._leaflet_pos = pt;
-
-            if (pt.x + containerSize.x > mapSize.x) {
-                container.style.left = 'auto';
-                container.style.right = Math.max(mapSize.x - pt.x, 0) + 'px';
-            } else {
-                container.style.left = Math.max(pt.x, 0) + 'px';
-                container.style.right = 'auto';
-            }
-
-            if (pt.y + containerSize.y > mapSize.y) {
-                container.style.top = 'auto';
-                container.style.bottom = Math.max(mapSize.y - pt.y, 0) + 'px';
-            } else {
-                container.style.top = Math.max(pt.y, 0) + 'px';
-                container.style.bottom = 'auto';
-            }
-        },
-
-        _getElementSize : function (el) {
-            var size = this._size,
-            initialDisplay = el.style.display;
-
-            if (!size || this._sizeChanged) {
-                size = {};
-
-                el.style.left = '-999999px';
-                el.style.right = 'auto';
-                el.style.display = 'block';
-
-                size.x = el.offsetWidth;
-                size.y = el.offsetHeight;
-
-                el.style.left = 'auto';
-                el.style.display = initialDisplay;
-
-                this._sizeChanged = false;
-            }
-
-            return size;
-        },
-
-        _onMouseDown : function (e) {
-            //console.log('_onMouseDown');
-            this._hide();
-        },
-
-        _onKeyDown : function (e) {
-            var key = e.keyCode;
-
-            // If ESC pressed and context menu is visible hide it
-            if (key === 27) {
-                this._hide();
-            }
-        },
-
-        _onItemMouseOver : function (e) {
-            L.DomUtil.addClass(e.target, 'over');
-        },
-
-        _onItemMouseOut : function (e) {
-            L.DomUtil.removeClass(e.target, 'over');
-        }
-    });
-
-L.Map.addInitHook('addHandler', 'contextmenu', L.Map.ContextMenu);
-L.Mixin.ContextMenu = {
+	statics: {
+		BASE_CLS: 'leaflet-contextmenu'
+	},
 
-    // private methods
-    _initContextMenu : function () {
-        this._items = [];
+	initialize: function (map) {
+		L.Handler.prototype.initialize.call(this, map);
 
-        this.on('contextmenu', this._showContextMenu, this);
-    },
+		this._items = [];
+		this._visible = false;
 
-    _showContextMenu : function (e) {
-        var itemOptions,
-        pt,
-        i,
-        l;
+		var container = this._container = L.DomUtil.create('div', L.Map.ContextMenu.BASE_CLS, map._container);
+		container.style.zIndex = 10000;
+		container.style.position = 'absolute';
 
-        if (this._map.contextmenu) {
-            pt = this._map.mouseEventToContainerPoint(e.originalEvent);
+		if (map.options.contextmenuWidth) {
+			container.style.width = map.options.contextmenuWidth + 'px';
+		}
+		
+		this._createItems();
 
-            for (i = 0, l = this.options.contextmenuItems.length; i < l; i++) {
-                itemOptions = this.options.contextmenuItems[i];
-                this._items.push(this._map.contextmenu.insertItem(itemOptions, itemOptions.index));
-            }
+		L.DomEvent
+			.on(container, 'click', L.DomEvent.stop)
+			.on(container, 'mousedown', L.DomEvent.stop)
+			.on(container, 'dblclick', L.DomEvent.stop)
+			.on(container, 'contextmenu', L.DomEvent.stop);
+	},
 
-            this._map.once('contextmenu.hide', this._hideContextMenu, this);
+	addHooks: function () {
+        var container = this._map.getContainer();
+        
+		L.DomEvent
+            .on(container, 'mouseleave', this._hide, this)
+			.on(document, 'keydown', this._onKeyDown, this);
 
-            this._map.contextmenu.showAt(pt, {
-                relatedTarget : this
-            },
-            this.options.contextmenuAtiveState);
+        if (L.Browser.touch) {
+            L.DomEvent.on(document, this._touchstart, this._hide, this);
         }
-    },
-
-    _hideContextMenu : function () {
-        var i,
-        l;
-
-        for (i = 0, l = this._items.length; i < l; i++) {
-            this._map.contextmenu.removeItem(this._items[i]);
+        
+		this._map.on({
+			contextmenu: this._show,
+			mousedown: this._hide,
+			movestart: this._hide,
+			zoomstart: this._hide
+		}, this);
+	},
+
+	removeHooks: function () {
+        var container = this._map.getContainer();
+        
+		L.DomEvent
+            .off(container, 'mouseleave', this._hide, this)			
+			.off(document, 'keydown', this._onKeyDown, this);
+
+        if (L.Browser.touch) {
+            L.DomEvent.off(document, this._touchstart, this._hide, this);
+        }        
+
+		this._map.off({
+			contextmenu: this._show,
+			mousedown: this._hide,
+			movestart: this._hide,
+			zoomstart: this._hide
+		}, this);
+	},
+
+	showAt: function (point, data) {
+		if (point instanceof L.LatLng) {
+			point = this._map.latLngToContainerPoint(point);
+		}
+		this._showAtPoint(point, data);
+	},
+
+	hide: function () {
+		this._hide();
+	},
+
+	addItem: function (options) {
+		return this.insertItem(options);
+	},
+
+	insertItem: function (options, index) {
+		index = index !== undefined ? index: this._items.length; 
+
+		var item = this._createItem(this._container, options, index);
+		
+		this._items.push(item);
+
+		this._sizeChanged = true;
+
+		this._map.fire('contextmenu.additem', {
+			contextmenu: this,
+			el: item.el,
+			index: index
+		});
+
+		return item.el;
+	},
+
+	removeItem: function (item) {
+		var container = this._container;
+
+		if (!isNaN(item)) {
+			item = container.children[item];
+		}
+
+		if (item) {
+			this._removeItem(L.Util.stamp(item));
+
+			this._sizeChanged = true;
+
+			this._map.fire('contextmenu.removeitem', {
+				contextmenu: this,
+				el: item
+			});
+		}		
+	},
+
+	removeAllItems: function () {
+		var item;
+
+		while (this._container.children.length) {
+			item = this._container.children[0];
+			this._removeItem(L.Util.stamp(item));
+		}
+	},
+
+	hideAllItems: function () {
+		var item, i, l;
+
+		for (i = 0, l = this._items.length; i < l; i++) {
+			item = this._items[i];
+			item.el.style.display = 'none';
+		}
+	},
+
+	showAllItems: function () {
+		var item, i, l;
+
+		for (i = 0, l = this._items.length; i < l; i++) {
+			item = this._items[i];
+			item.el.style.display = '';
+		}		
+	},
+
+	setDisabled: function (item, disabled) {
+		var container = this._container,
+		itemCls = L.Map.ContextMenu.BASE_CLS + '-item';
+
+		if (!isNaN(item)) {
+			item = container.children[item];
+		}
+
+		if (item && L.DomUtil.hasClass(item, itemCls)) {
+			if (disabled) {
+				L.DomUtil.addClass(item, itemCls + '-disabled');
+				this._map.fire('contextmenu.disableitem', {
+					contextmenu: this,
+					el: item
+				});
+			} else {
+				L.DomUtil.removeClass(item, itemCls + '-disabled');
+				this._map.fire('contextmenu.enableitem', {
+					contextmenu: this,
+					el: item
+				});
+			}			
+		}
+	},
+
+	isVisible: function () {
+		return this._visible;
+	},
+
+	_createItems: function () {
+		var itemOptions = this._map.options.contextmenuItems,
+		    item,
+		    i, l;
+
+		for (i = 0, l = itemOptions.length; i < l; i++) {
+			this._items.push(this._createItem(this._container, itemOptions[i]));
+		}
+	},
+
+	_createItem: function (container, options, index) {
+		if (options.separator || options === '-') {
+			return this._createSeparator(container, index);
+		}
+
+		var itemCls = L.Map.ContextMenu.BASE_CLS + '-item', 
+		    cls = options.disabled ? (itemCls + ' ' + itemCls + '-disabled') : itemCls,
+		    el = this._insertElementAt('a', cls, container, index),
+		    callback = this._createEventHandler(el, options.callback, options.context, options.hideOnSelect),
+		    html = '';
+		
+		if (options.icon) {
+			html = '<img class="' + L.Map.ContextMenu.BASE_CLS + '-icon" src="' + options.icon + '"/>';
+		} else if (options.iconCls) {
+			html = '<span class="' + L.Map.ContextMenu.BASE_CLS + '-icon ' + options.iconCls + '"></span>';
+		}
+
+		el.innerHTML = html + options.text;		
+		el.href = '#';
+
+		L.DomEvent
+			.on(el, 'mouseover', this._onItemMouseOver, this)
+			.on(el, 'mouseout', this._onItemMouseOut, this)
+			.on(el, 'mousedown', L.DomEvent.stopPropagation)
+			.on(el, 'click', callback);
+
+        if (L.Browser.touch) {
+            L.DomEvent.on(el, this._touchstart, L.DomEvent.stopPropagation);
         }
-        this._items.length = 0;
-    }
-};
-
-L.Marker.mergeOptions({
-    contextmenu : false,
-    contextmenuItems : []
-});
 
-L.Marker.addInitHook(function () {
-    if (this.options.contextmenu) {
-        this._initContextMenu();
-    }
+		return {
+			id: L.Util.stamp(el),
+			el: el,
+			callback: callback
+		};
+	},
+
+	_removeItem: function (id) {
+		var item,
+		    el,
+		    i, l, callback;
+
+		for (i = 0, l = this._items.length; i < l; i++) {
+			item = this._items[i];
+
+			if (item.id === id) {
+				el = item.el;
+				callback = item.callback;
+
+				if (callback) {
+					L.DomEvent
+						.off(el, 'mouseover', this._onItemMouseOver, this)
+						.off(el, 'mouseover', this._onItemMouseOut, this)
+						.off(el, 'mousedown', L.DomEvent.stopPropagation)
+						.off(el, 'click', callback);
+
+                    if (L.Browser.touch) {
+                        L.DomEvent.off(el, this._touchstart, L.DomEvent.stopPropagation);
+                    }
+				}
+				
+				this._container.removeChild(el);
+				this._items.splice(i, 1);
+
+				return item;
+			}
+		}
+		return null;
+	},
+
+	_createSeparator: function (container, index) {
+		var el = this._insertElementAt('div', L.Map.ContextMenu.BASE_CLS + '-separator', container, index);
+		
+		return {
+			id: L.Util.stamp(el),
+			el: el
+		};
+	},
+
+	_createEventHandler: function (el, func, context, hideOnSelect) {
+		var me = this,
+		    map = this._map,
+		    disabledCls = L.Map.ContextMenu.BASE_CLS + '-item-disabled',
+		    hideOnSelect = (hideOnSelect !== undefined) ? hideOnSelect : true;
+		
+		return function (e) {
+			if (L.DomUtil.hasClass(el, disabledCls)) {
+				return;
+			}
+			
+			if (hideOnSelect) {
+				me._hide();			
+			}
+
+			if (func) {
+				func.call(context || map, me._showLocation);			
+			}
+
+			me._map.fire('contextmenu:select', {
+				contextmenu: me,
+				el: el
+			});
+		};
+	},
+
+	_insertElementAt: function (tagName, className, container, index) {
+		var refEl,
+		    el = document.createElement(tagName);
+
+		el.className = className;
+
+		if (index !== undefined) {
+			refEl = container.children[index];
+		}
+
+		if (refEl) {
+			container.insertBefore(el, refEl);
+		} else {
+			container.appendChild(el);
+		}
+
+		return el;
+	},
+
+	_show: function (e) {
+		this._showAtPoint(e.containerPoint, e);
+	},
+
+	_showAtPoint: function (pt, data) {
+		if (this._items.length) {
+			var map = this._map,
+			layerPoint = map.containerPointToLayerPoint(pt),
+			latlng = map.layerPointToLatLng(layerPoint),
+			event = L.extend(data || {}, {contextmenu: this});
+			
+			this._showLocation = {
+				latlng: latlng,
+				layerPoint: layerPoint,
+				containerPoint: pt
+			};
+
+			if(data && data.relatedTarget){
+				this._showLocation.relatedTarget = data.relatedTarget;
+			}
+
+			this._setPosition(pt);			
+
+			if (!this._visible) {
+				this._container.style.display = 'block';							
+				this._visible = true;							
+			} else {
+				this._setPosition(pt);			
+			}
+
+			this._map.fire('contextmenu.show', event);
+		}
+	},
+
+	_hide: function () {        
+		if (this._visible) {
+			this._visible = false;
+			this._container.style.display = 'none';
+			this._map.fire('contextmenu.hide', {contextmenu: this});
+		}
+	},
+
+	_setPosition: function (pt) {
+		var mapSize = this._map.getSize(),
+		    container = this._container,
+		    containerSize = this._getElementSize(container),
+		    anchor;
+
+		if (this._map.options.contextmenuAnchor) {
+			anchor = L.point(this._map.options.contextmenuAnchor);
+			pt = pt.add(anchor);
+		}
+
+		container._leaflet_pos = pt;
+
+		if (pt.x + containerSize.x > mapSize.x) {
+			container.style.left = 'auto';
+			container.style.right = Math.max(mapSize.x - pt.x, 0) + 'px';
+		} else {
+			container.style.left = Math.max(pt.x, 0) + 'px';
+			container.style.right = 'auto';
+		}
+		
+		if (pt.y + containerSize.y > mapSize.y) {
+			container.style.top = 'auto';
+			container.style.bottom = Math.max(mapSize.y - pt.y, 0) + 'px';
+		} else {
+			container.style.top = Math.max(pt.y, 0) + 'px';
+			container.style.bottom = 'auto';
+		}
+	},
+
+	_getElementSize: function (el) {		
+		var size = this._size,
+		    initialDisplay = el.style.display;
+
+		if (!size || this._sizeChanged) {
+			size = {};
+
+			el.style.left = '-999999px';
+			el.style.right = 'auto';
+			el.style.display = 'block';
+			
+			size.x = el.offsetWidth;
+			size.y = el.offsetHeight;
+			
+			el.style.left = 'auto';
+			el.style.display = initialDisplay;
+			
+			this._sizeChanged = false;
+		}
+
+		return size;
+	},
+
+	_onKeyDown: function (e) {
+		var key = e.keyCode;
+
+		// If ESC pressed and context menu is visible hide it 
+		if (key === 27) {
+			this._hide();
+		}
+	},
+
+	_onItemMouseOver: function (e) {
+		L.DomUtil.addClass(e.target || e.srcElement, 'over');
+	},
+
+	_onItemMouseOut: function (e) {
+		L.DomUtil.removeClass(e.target || e.srcElement, 'over');
+	}
 });
 
-L.Marker.include(L.Mixin.ContextMenu);
-
-L.Path.mergeOptions({
-    contextmenu : false,
-    contextmenuItems : []
-});
+L.Map.addInitHook('addHandler', 'contextmenu', L.Map.ContextMenu);
+L.Mixin.ContextMenu = {
 
-L.Path.addInitHook(function () {
-    if (this.options.contextmenu) {
-        this._initContextMenu();
-    }
-});
+	bindContextMenu: function (options) {
+		L.setOptions(this, options);
+		this._initContextMenu();
+
+		return this;
+	},
+
+	unbindContextMenu: function (){
+		this.off('contextmenu', this._showContextMenu, this);
+
+		return this;
+	},
+
+	addContextMenuItem: function (item) {
+			this.options.contextmenuItems.push(item);
+	},
+
+	removeContextMenuItemWithIndex: function (index) {
+		  var items = [];
+			for (var i = 0; i < this.options.contextmenuItems.length; i++) {
+					if(this.options.contextmenuItems[i].index == index){
+							items.push(i);
+					}
+			}
+			var elem = items.pop();
+			while (elem !== undefined) {
+				  this.options.contextmenuItems.splice(elem,1);
+					elem = items.pop();
+		  }
+	},
+
+	replaceConextMenuItem: function (item) {
+		  this.removeContextMenuItemWithIndex(item.index);
+		  this.addContextMenuItem(item);
+	},
+
+	_initContextMenu: function () {
+		this._items = [];
+	
+		this.on('contextmenu', this._showContextMenu, this);
+	},
+
+	_showContextMenu: function (e) {
+		var itemOptions,
+		    data, pt, i, l;
+
+		if (this._map.contextmenu) {
+            data = L.extend({relatedTarget: this}, e)
+            
+			pt = this._map.mouseEventToContainerPoint(e.originalEvent);
+
+			if (!this.options.contextmenuInheritItems) {
+				this._map.contextmenu.hideAllItems();
+			}
+
+			for (i = 0, l = this.options.contextmenuItems.length; i < l; i++) {
+				itemOptions = this.options.contextmenuItems[i];
+				this._items.push(this._map.contextmenu.insertItem(itemOptions, itemOptions.index));
+			}
+
+			this._map.once('contextmenu.hide', this._hideContextMenu, this);
+		
+			this._map.contextmenu.showAt(pt, data);
+		}
+	},
+
+	_hideContextMenu: function () {
+		var i, l;
+
+		for (i = 0, l = this._items.length; i < l; i++) {
+			this._map.contextmenu.removeItem(this._items[i]);
+		}
+		this._items.length = 0;		
+
+		if (!this.options.contextmenuInheritItems) {
+			this._map.contextmenu.showAllItems();
+		}
+	}	
+};
 
-L.Path.include(L.Mixin.ContextMenu);
\ No newline at end of file
+var classes = [L.Marker, L.Path],
+    defaultOptions = {
+		contextmenu: false,
+		contextmenuItems: [],
+	    contextmenuInheritItems: true
+	},
+    cls, i, l;
+
+for (i = 0, l = classes.length; i < l; i++) {
+	cls = classes[i];
+
+	// L.Class should probably provide an empty options hash, as it does not test
+	// for it here and add if needed
+	if (!cls.prototype.options) {
+		cls.prototype.options = defaultOptions;
+	} else {
+		cls.mergeOptions(defaultOptions);
+	}
+
+	cls.addInitHook(function () {
+		if (this.options.contextmenu) {
+			this._initContextMenu();
+		}
+	});
+
+	cls.include(L.Mixin.ContextMenu);
+}
+	return L.Map.ContextMenu;
+	});
diff --git a/web/src/main/webapp/js/main-template.js b/web/src/main/webapp/js/main-template.js
index 8630acbea..15e9ab172 100644
--- a/web/src/main/webapp/js/main-template.js
+++ b/web/src/main/webapp/js/main-template.js
@@ -315,14 +315,14 @@ function checkInput() {
 }
 
 function setToStart(e) {
-    var latlng = e.target.getLatLng(),
+    var latlng = e.relatedTarget.getLatLng(),
             index = ghRequest.route.getIndexByCoord(latlng);
     ghRequest.route.move(index, 0);
     routeIfAllResolved();
 }
 
 function setToEnd(e) {
-    var latlng = e.target.getLatLng(),
+    var latlng = e.relatedTarget.getLatLng(),
             index = ghRequest.route.getIndexByCoord(latlng);
     ghRequest.route.move(index, -1);
     routeIfAllResolved();
@@ -351,7 +351,7 @@ function setIntermediateCoord(e) {
 }
 
 function deleteCoord(e) {
-    var latlng = e.target.getLatLng();
+    var latlng = e.relatedTarget.getLatLng();
     ghRequest.route.removeSingle(latlng);
     mapLayer.clearLayers();
     routeLatLng(ghRequest, false);
@@ -393,11 +393,10 @@ function setFlag(coord, index) {
         var _tempItem = {
             text: translate.tr('set_start'),
             callback: setToStart,
-            index: 1,
-            state: 2
+            index: 1
         };
         if (toFrom === -1)
-            marker.options.contextmenuItems.push(_tempItem);// because the Mixin.ContextMenu isn't initialized
+            marker.options.contextmenuItems.push(_tempItem); // because the Mixin.ContextMenu isn't initialized
         marker.on('dragend', function (e) {
             mapLayer.clearLayers();
             // inconsistent leaflet API: event.target.getLatLng vs. mouseEvent.latlng?
@@ -545,6 +544,16 @@ function routeLatLng(request, doQuery) {
         var geoJsons = [];
         var firstHeader;
 
+        // Create buttons to toggle between SI and imperial units.
+        var createUnitsChooserButtonClickHandler = function (useMiles) {
+            return function () {
+                mapLayer.updateScale(useMiles);
+                ghRequest.useMiles = useMiles;
+                resolveAll();
+                routeLatLng(ghRequest);
+            };
+        };
+
         for (var pathIndex = 0; pathIndex < json.paths.length; pathIndex++) {
             var tabHeader = $("<li>").append((pathIndex + 1) + "<img class='alt_route_img' src='img/alt_route.png'/>");
             if (pathIndex === 0)
@@ -579,15 +588,6 @@ function routeLatLng(request, doQuery) {
             }
             routeInfo.append(translate.tr("route_info", [tmpDist, tmpTime]));
 
-            //create buttons to toggle between si and imperial units
-            var createUnitsChooserButtonClickHandler = function (useMiles) {
-                return function () {
-                    mapLayer.updateScale(useMiles);
-                    ghRequest.useMiles = useMiles;
-                    resolveAll();
-                    routeLatLng(ghRequest);
-                };
-            };
             var kmButton = $("<button class='plain_text_button " + (request.useMiles ? "gray" : "") + "'>");
             kmButton.text(translate.tr2("km_abbr"));
             kmButton.click(createUnitsChooserButtonClickHandler(false));
diff --git a/web/src/main/webapp/js/main.js b/web/src/main/webapp/js/main.js
index d8eaef1bb..96b82be4e 100644
--- a/web/src/main/webapp/js/main.js
+++ b/web/src/main/webapp/js/main.js
@@ -56,9 +56,9 @@ at:"center",of:window,collision:"fit",using:function(e){var i=t(this).css(e).off
 !function(e,t){"use strict";var r=e.History=e.History||{},a=e.jQuery;if("undefined"!=typeof r.Adapter)throw new Error("History.js Adapter has already been loaded...");r.Adapter={bind:function(e,t,r){a(e).bind(t,r)},trigger:function(e,t,r){a(e).trigger(t,r)},extractEventData:function(e,r,a){var n=r&&r.originalEvent&&r.originalEvent[e]||a&&a[e]||t;return n},onDomLoad:function(e){a(e)}},"undefined"!=typeof r.init&&r.init()}(window),function(e,t){"use strict";var r=e.console||t,a=e.document,n=e.navigator,o=e.sessionStorage||!1,i=e.setTimeout,s=e.clearTimeout,u=e.setInterval,l=e.clearInterval,d=e.JSON,c=e.alert,p=e.History=e.History||{},f=e.history;try{o.setItem("TEST","1"),o.removeItem("TEST")}catch(e){o=!1}if(d.stringify=d.stringify||d.encode,d.parse=d.parse||d.decode,"undefined"!=typeof p.init)throw new Error("History.js Core has already been loaded...");p.init=function(e){return"undefined"!=typeof p.Adapter&&("undefined"!=typeof p.initCore&&p.initCore(),"undefined"!=typeof p.initHtml4&&p.initHtml4(),!0)},p.initCore=function(g){if("undefined"!=typeof p.initCore.initialized)return!1;if(p.initCore.initialized=!0,p.options=p.options||{},p.options.hashChangeInterval=p.options.hashChangeInterval||100,p.options.safariPollInterval=p.options.safariPollInterval||500,p.options.doubleCheckInterval=p.options.doubleCheckInterval||500,p.options.disableSuid=p.options.disableSuid||!1,p.options.storeInterval=p.options.storeInterval||1e3,p.options.busyDelay=p.options.busyDelay||250,p.options.debug=p.options.debug||!1,p.options.initialTitle=p.options.initialTitle||a.title,p.options.html4Mode=p.options.html4Mode||!1,p.options.delayInit=p.options.delayInit||!1,p.intervalList=[],p.clearAllIntervals=function(){var e,t=p.intervalList;if("undefined"!=typeof t&&null!==t){for(e=0;e<t.length;e++)l(t[e]);p.intervalList=null}},p.debug=function(){(p.options.debug||!1)&&p.log.apply(p,arguments)},p.log=function(){var e,t,n,o,i,s="undefined"!=typeof r&&"undefined"!=typeof r.log&&"undefined"!=typeof r.log.apply,u=a.getElementById("log");for(s?(o=Array.prototype.slice.call(arguments),e=o.shift(),"undefined"!=typeof r.debug?r.debug.apply(r,[e,o]):r.log.apply(r,[e,o])):e="\n"+arguments[0]+"\n",t=1,n=arguments.length;t<n;++t){if(i=arguments[t],"object"==typeof i&&"undefined"!=typeof d)try{i=d.stringify(i)}catch(e){}e+="\n"+i+"\n"}return u?(u.value+=e+"\n-----\n",u.scrollTop=u.scrollHeight-u.clientHeight):s||c(e),!0},p.getInternetExplorerMajorVersion=function(){var e=p.getInternetExplorerMajorVersion.cached="undefined"!=typeof p.getInternetExplorerMajorVersion.cached?p.getInternetExplorerMajorVersion.cached:function(){for(var e=3,t=a.createElement("div"),r=t.getElementsByTagName("i");(t.innerHTML="<!--[if gt IE "+ ++e+"]><i></i><![endif]-->")&&r[0];);return e>4&&e}();return e},p.isInternetExplorer=function(){var e=p.isInternetExplorer.cached="undefined"!=typeof p.isInternetExplorer.cached?p.isInternetExplorer.cached:Boolean(p.getInternetExplorerMajorVersion());return e},p.options.html4Mode?p.emulated={pushState:!0,hashChange:!0}:p.emulated={pushState:!Boolean(e.history&&e.history.pushState&&e.history.replaceState&&!/ Mobile\/([1-7][a-z]|(8([abcde]|f(1[0-8]))))/i.test(n.userAgent)&&!/AppleWebKit\/5([0-2]|3[0-2])/i.test(n.userAgent)),hashChange:Boolean(!("onhashchange"in e||"onhashchange"in a)||p.isInternetExplorer()&&p.getInternetExplorerMajorVersion()<8)},p.enabled=!p.emulated.pushState,p.bugs={setHash:Boolean(!p.emulated.pushState&&"Apple Computer, Inc."===n.vendor&&/AppleWebKit\/5([0-2]|3[0-3])/.test(n.userAgent)),safariPoll:Boolean(!p.emulated.pushState&&"Apple Computer, Inc."===n.vendor&&/AppleWebKit\/5([0-2]|3[0-3])/.test(n.userAgent)),ieDoubleCheck:Boolean(p.isInternetExplorer()&&p.getInternetExplorerMajorVersion()<8),hashEscape:Boolean(p.isInternetExplorer()&&p.getInternetExplorerMajorVersion()<7)},p.isEmptyObject=function(e){for(var t in e)if(e.hasOwnProperty(t))return!1;return!0},p.cloneObject=function(e){var t,r;return e?(t=d.stringify(e),r=d.parse(t)):r={},r},p.getRootUrl=function(){var e=a.location.protocol+"//"+(a.location.hostname||a.location.host);return a.location.port&&(e+=":"+a.location.port),e+="/"},p.getBaseHref=function(){var e=a.getElementsByTagName("base"),t=null,r="";return 1===e.length&&(t=e[0],r=t.href.replace(/[^\/]+$/,"")),r=r.replace(/\/+$/,""),r&&(r+="/"),r},p.getBaseUrl=function(){var e=p.getBaseHref()||p.getBasePageUrl()||p.getRootUrl();return e},p.getPageUrl=function(){var e,t=p.getState(!1,!1),r=(t||{}).url||p.getLocationHref();return e=r.replace(/\/+$/,"").replace(/[^\/]+$/,function(e,t,r){return/\./.test(e)?e:e+"/"})},p.getBasePageUrl=function(){var e=p.getLocationHref().replace(/[#\?].*/,"").replace(/[^\/]+$/,function(e,t,r){return/[^\/]$/.test(e)?"":e}).replace(/\/+$/,"")+"/";return e},p.getFullUrl=function(e,t){var r=e,a=e.substring(0,1);return t="undefined"==typeof t||t,/[a-z]+\:\/\//.test(e)||(r="/"===a?p.getRootUrl()+e.replace(/^\/+/,""):"#"===a?p.getPageUrl().replace(/#.*/,"")+e:"?"===a?p.getPageUrl().replace(/[\?#].*/,"")+e:t?p.getBaseUrl()+e.replace(/^(\.\/)+/,""):p.getBasePageUrl()+e.replace(/^(\.\/)+/,"")),r.replace(/\#$/,"")},p.getShortUrl=function(e){var t=e,r=p.getBaseUrl(),a=p.getRootUrl();return p.emulated.pushState&&(t=t.replace(r,"")),t=t.replace(a,"/"),p.isTraditionalAnchor(t)&&(t="./"+t),t=t.replace(/^(\.\/)+/g,"./").replace(/\#$/,"")},p.getLocationHref=function(e){return e=e||a,e.URL===e.location.href?e.location.href:e.location.href===decodeURIComponent(e.URL)?e.URL:e.location.hash&&decodeURIComponent(e.location.href.replace(/^[^#]+/,""))===e.location.hash?e.location.href:e.URL.indexOf("#")==-1&&e.location.href.indexOf("#")!=-1?e.location.href:e.URL||e.location.href},p.store={},p.idToState=p.idToState||{},p.stateToId=p.stateToId||{},p.urlToId=p.urlToId||{},p.storedStates=p.storedStates||[],p.savedStates=p.savedStates||[],p.normalizeStore=function(){p.store.idToState=p.store.idToState||{},p.store.urlToId=p.store.urlToId||{},p.store.stateToId=p.store.stateToId||{}},p.getState=function(e,t){"undefined"==typeof e&&(e=!0),"undefined"==typeof t&&(t=!0);var r=p.getLastSavedState();return!r&&t&&(r=p.createStateObject()),e&&(r=p.cloneObject(r),r.url=r.cleanUrl||r.url),r},p.getIdByState=function(e){var t,r=p.extractId(e.url);if(!r)if(t=p.getStateString(e),"undefined"!=typeof p.stateToId[t])r=p.stateToId[t];else if("undefined"!=typeof p.store.stateToId[t])r=p.store.stateToId[t];else{for(;r=(new Date).getTime()+String(Math.random()).replace(/\D/g,""),"undefined"!=typeof p.idToState[r]||"undefined"!=typeof p.store.idToState[r];);p.stateToId[t]=r,p.idToState[r]=e}return r},p.normalizeState=function(e){var t,r;return e&&"object"==typeof e||(e={}),"undefined"!=typeof e.normalized?e:(e.data&&"object"==typeof e.data||(e.data={}),t={},t.normalized=!0,t.title=e.title||"",t.url=p.getFullUrl(e.url?e.url:p.getLocationHref()),t.hash=p.getShortUrl(t.url),t.data=p.cloneObject(e.data),t.id=p.getIdByState(t),t.cleanUrl=t.url.replace(/\??\&_suid.*/,""),t.url=t.cleanUrl,r=!p.isEmptyObject(t.data),(t.title||r)&&p.options.disableSuid!==!0&&(t.hash=p.getShortUrl(t.url).replace(/\??\&_suid.*/,""),/\?/.test(t.hash)||(t.hash+="?"),t.hash+="&_suid="+t.id),t.hashedUrl=p.getFullUrl(t.hash),(p.emulated.pushState||p.bugs.safariPoll)&&p.hasUrlDuplicate(t)&&(t.url=t.hashedUrl),t)},p.createStateObject=function(e,t,r){var a={data:e,title:t,url:r};return a=p.normalizeState(a)},p.getStateById=function(e){e=String(e);var r=p.idToState[e]||p.store.idToState[e]||t;return r},p.getStateString=function(e){var t,r,a;return t=p.normalizeState(e),r={data:t.data,title:e.title,url:e.url},a=d.stringify(r)},p.getStateId=function(e){var t,r;return t=p.normalizeState(e),r=t.id},p.getHashByState=function(e){var t,r;return t=p.normalizeState(e),r=t.hash},p.extractId=function(e){var t,r,a,n;return n=e.indexOf("#")!=-1?e.split("#")[0]:e,r=/(.*)\&_suid=([0-9]+)$/.exec(n),a=r?r[1]||e:e,t=r?String(r[2]||""):"",t||!1},p.isTraditionalAnchor=function(e){var t=!/[\/\?\.]/.test(e);return t},p.extractState=function(e,t){var r,a,n=null;return t=t||!1,r=p.extractId(e),r&&(n=p.getStateById(r)),n||(a=p.getFullUrl(e),r=p.getIdByUrl(a)||!1,r&&(n=p.getStateById(r)),!n&&t&&!p.isTraditionalAnchor(e)&&(n=p.createStateObject(null,null,a))),n},p.getIdByUrl=function(e){var r=p.urlToId[e]||p.store.urlToId[e]||t;return r},p.getLastSavedState=function(){return p.savedStates[p.savedStates.length-1]||t},p.getLastStoredState=function(){return p.storedStates[p.storedStates.length-1]||t},p.hasUrlDuplicate=function(e){var t,r=!1;return t=p.extractState(e.url),r=t&&t.id!==e.id},p.storeState=function(e){return p.urlToId[e.url]=e.id,p.storedStates.push(p.cloneObject(e)),e},p.isLastSavedState=function(e){var t,r,a,n=!1;return p.savedStates.length&&(t=e.id,r=p.getLastSavedState(),a=r.id,n=t===a),n},p.saveState=function(e){return!p.isLastSavedState(e)&&(p.savedStates.push(p.cloneObject(e)),!0)},p.getStateByIndex=function(e){var t=null;return t="undefined"==typeof e?p.savedStates[p.savedStates.length-1]:e<0?p.savedStates[p.savedStates.length+e]:p.savedStates[e]},p.getCurrentIndex=function(){var e=null;return e=p.savedStates.length<1?0:p.savedStates.length-1},p.getHash=function(e){var t,r=p.getLocationHref(e);return t=p.getHashByUrl(r)},p.unescapeHash=function(e){var t=p.normalizeHash(e);return t=decodeURIComponent(t)},p.normalizeHash=function(e){var t=e.replace(/[^#]*#/,"").replace(/#.*/,"");return t},p.setHash=function(e,t){var r,n;return t!==!1&&p.busy()?(p.pushQueue({scope:p,callback:p.setHash,args:arguments,queue:t}),!1):(p.busy(!0),r=p.extractState(e,!0),r&&!p.emulated.pushState?p.pushState(r.data,r.title,r.url,!1):p.getHash()!==e&&(p.bugs.setHash?(n=p.getPageUrl(),p.pushState(null,null,n+"#"+e,!1)):a.location.hash=e),p)},p.escapeHash=function(t){var r=p.normalizeHash(t);return r=e.encodeURIComponent(r),p.bugs.hashEscape||(r=r.replace(/\%21/g,"!").replace(/\%26/g,"&").replace(/\%3D/g,"=").replace(/\%3F/g,"?")),r},p.getHashByUrl=function(e){var t=String(e).replace(/([^#]*)#?([^#]*)#?(.*)/,"$2");return t=p.unescapeHash(t)},p.setTitle=function(e){var t,r=e.title;r||(t=p.getStateByIndex(0),t&&t.url===e.url&&(r=t.title||p.options.initialTitle));try{a.getElementsByTagName("title")[0].innerHTML=r.replace("<","&lt;").replace(">","&gt;").replace(" & "," &amp; ")}catch(e){}return a.title=r,p},p.queues=[],p.busy=function(e){if("undefined"!=typeof e?p.busy.flag=e:"undefined"==typeof p.busy.flag&&(p.busy.flag=!1),!p.busy.flag){s(p.busy.timeout);var t=function(){var e,r,a;if(!p.busy.flag)for(e=p.queues.length-1;e>=0;--e)r=p.queues[e],0!==r.length&&(a=r.shift(),p.fireQueueItem(a),p.busy.timeout=i(t,p.options.busyDelay))};p.busy.timeout=i(t,p.options.busyDelay)}return p.busy.flag},p.busy.flag=!1,p.fireQueueItem=function(e){return e.callback.apply(e.scope||p,e.args||[])},p.pushQueue=function(e){return p.queues[e.queue||0]=p.queues[e.queue||0]||[],p.queues[e.queue||0].push(e),p},p.queue=function(e,t){return"function"==typeof e&&(e={callback:e}),"undefined"!=typeof t&&(e.queue=t),p.busy()?p.pushQueue(e):p.fireQueueItem(e),p},p.clearQueue=function(){return p.busy.flag=!1,p.queues=[],p},p.stateChanged=!1,p.doubleChecker=!1,p.doubleCheckComplete=function(){return p.stateChanged=!0,p.doubleCheckClear(),p},p.doubleCheckClear=function(){return p.doubleChecker&&(s(p.doubleChecker),p.doubleChecker=!1),p},p.doubleCheck=function(e){return p.stateChanged=!1,p.doubleCheckClear(),p.bugs.ieDoubleCheck&&(p.doubleChecker=i(function(){return p.doubleCheckClear(),p.stateChanged||e(),!0},p.options.doubleCheckInterval)),p},p.safariStatePoll=function(){var t,r=p.extractState(p.getLocationHref());if(!p.isLastSavedState(r))return t=r,t||(t=p.createStateObject()),p.Adapter.trigger(e,"popstate"),p},p.back=function(e){return e!==!1&&p.busy()?(p.pushQueue({scope:p,callback:p.back,args:arguments,queue:e}),!1):(p.busy(!0),p.doubleCheck(function(){p.back(!1)}),f.go(-1),!0)},p.forward=function(e){return e!==!1&&p.busy()?(p.pushQueue({scope:p,callback:p.forward,args:arguments,queue:e}),!1):(p.busy(!0),p.doubleCheck(function(){p.forward(!1)}),f.go(1),!0)},p.go=function(e,t){var r;if(e>0)for(r=1;r<=e;++r)p.forward(t);else{if(!(e<0))throw new Error("History.go: History.go requires a positive or negative integer passed.");for(r=-1;r>=e;--r)p.back(t)}return p},p.emulated.pushState){var h=function(){};p.pushState=p.pushState||h,p.replaceState=p.replaceState||h}else p.onPopState=function(t,r){var a,n,o=!1,i=!1;return p.doubleCheckComplete(),a=p.getHash(),a?(n=p.extractState(a||p.getLocationHref(),!0),n?p.replaceState(n.data,n.title,n.url,!1):(p.Adapter.trigger(e,"anchorchange"),p.busy(!1)),p.expectedStateId=!1,!1):(o=p.Adapter.extractEventData("state",t,r)||!1,i=o?p.getStateById(o):p.expectedStateId?p.getStateById(p.expectedStateId):p.extractState(p.getLocationHref()),i||(i=p.createStateObject(null,null,p.getLocationHref())),p.expectedStateId=!1,p.isLastSavedState(i)?(p.busy(!1),!1):(p.storeState(i),p.saveState(i),p.setTitle(i),p.Adapter.trigger(e,"statechange"),p.busy(!1),!0))},p.Adapter.bind(e,"popstate",p.onPopState),p.pushState=function(t,r,a,n){if(p.getHashByUrl(a)&&p.emulated.pushState)throw new Error("History.js does not support states with fragement-identifiers (hashes/anchors).");if(n!==!1&&p.busy())return p.pushQueue({scope:p,callback:p.pushState,args:arguments,queue:n}),!1;p.busy(!0);var o=p.createStateObject(t,r,a);return p.isLastSavedState(o)?p.busy(!1):(p.storeState(o),p.expectedStateId=o.id,f.pushState(o.id,o.title,o.url),p.Adapter.trigger(e,"popstate")),!0},p.replaceState=function(t,r,a,n){if(p.getHashByUrl(a)&&p.emulated.pushState)throw new Error("History.js does not support states with fragement-identifiers (hashes/anchors).");if(n!==!1&&p.busy())return p.pushQueue({scope:p,callback:p.replaceState,args:arguments,queue:n}),!1;p.busy(!0);var o=p.createStateObject(t,r,a);return p.isLastSavedState(o)?p.busy(!1):(p.storeState(o),p.expectedStateId=o.id,f.replaceState(o.id,o.title,o.url),p.Adapter.trigger(e,"popstate")),!0};if(o){try{p.store=d.parse(o.getItem("History.store"))||{}}catch(e){p.store={}}p.normalizeStore()}else p.store={},p.normalizeStore();p.Adapter.bind(e,"unload",p.clearAllIntervals),p.saveState(p.storeState(p.extractState(p.getLocationHref(),!0))),o&&(p.onUnload=function(){var e,t,r;try{e=d.parse(o.getItem("History.store"))||{}}catch(t){e={}}e.idToState=e.idToState||{},e.urlToId=e.urlToId||{},e.stateToId=e.stateToId||{};for(t in p.idToState)p.idToState.hasOwnProperty(t)&&(e.idToState[t]=p.idToState[t]);for(t in p.urlToId)p.urlToId.hasOwnProperty(t)&&(e.urlToId[t]=p.urlToId[t]);for(t in p.stateToId)p.stateToId.hasOwnProperty(t)&&(e.stateToId[t]=p.stateToId[t]);p.store=e,p.normalizeStore(),r=d.stringify(e);try{o.setItem("History.store",r)}catch(e){if(e.code!==DOMException.QUOTA_EXCEEDED_ERR)throw e;o.length&&(o.removeItem("History.store"),o.setItem("History.store",r))}},p.intervalList.push(u(p.onUnload,p.options.storeInterval)),p.Adapter.bind(e,"beforeunload",p.onUnload),p.Adapter.bind(e,"unload",p.onUnload)),p.emulated.pushState||(p.bugs.safariPoll&&p.intervalList.push(u(p.safariStatePoll,p.options.safariPollInterval)),"Apple Computer, Inc."!==n.vendor&&"Mozilla"!==(n.appCodeName||"")||(p.Adapter.bind(e,"hashchange",function(){p.Adapter.trigger(e,"popstate")}),p.getHash()&&p.Adapter.onDomLoad(function(){p.Adapter.trigger(e,"hashchange")})))},(!p.options||!p.options.delayInit)&&p.init()}(window);
 
 },{}],17:[function(require,module,exports){
-L.Map.mergeOptions({contextmenuItems:[]}),L.Map.ContextMenu=L.Handler.extend({statics:{BASE_CLS:"leaflet-contextmenu"},initialize:function(t){L.Handler.prototype.initialize.call(this,t),this._items=[],this._sets=[],this._state=0,this._defaultState=t.options.contextmenuDefaultState||1,this._activeState=t.options.contextmenuAtiveState||1,this._visible=!1;var e=this._container=L.DomUtil.create("div",L.Map.ContextMenu.BASE_CLS,t._container);e.style.zIndex=1e4,e.style.position="absolute",t.options.contextmenuWidth&&(e.style.width=t.options.contextmenuWidth+"px"),void 0!==t.options.contextmenuSets&&0!==t.options.contextmenuSets.length||(t.options.contextmenuSets=[{name:"set_default",state:this._defaultState}]),this._createItems(),this._createSets(),this._changeState(),L.DomEvent.on(e,"click",L.DomEvent.stop).on(e,"mousedown",L.DomEvent.stop).on(e,"dblclick",L.DomEvent.stop).on(e,"contextmenu",L.DomEvent.stop)},addHooks:function(){L.DomEvent.on(document,L.Browser.touch?"touchstart":"mousedown",this._onMouseDown,this).on(document,"keydown",this._onKeyDown,this),this._map.on({contextmenu:this._show,mouseout:this._hide,mousedown:this._hide,movestart:this._hide,zoomstart:this._hide},this)},removeHooks:function(){L.DomEvent.off(document,"keydown",this._onKeyDown,this),this._map.off({contextmenu:this._show,mouseout:this._hide,mousedown:this._hide,movestart:this._hide,zoomstart:this._hide},this)},showAt:function(t,e,i){t instanceof L.LatLng&&(t=this._map.latLngToContainerPoint(t)),this._showAtPoint(t,e,i)},hide:function(){this._hide()},setState:function(t){return this._changeState(t)},setActiveState:function(t){var e,i,n,t=void 0!==t?t:this._activeState;for(i=0,n=this._sets.length;i<n;i++)if(e=this._sets[i],e.state===t){this._activeState=t;break}return e},getState:function(){return this._state},addSet:function(t){return this.insertSet(t)},insertSet:function(t,e){var e=void 0!==e?e:this._sets.length,i=this._createSet(t,e);return this._sets.push(i),i},addItem:function(t){return this.insertItem(t)},insertItem:function(t,e){var e=void 0!==e?e:this._items.length,i=this._createItem(this._container,t,e);return this._items.push(i),this._sizeChanged=!0,this._map.fire("contextmenu.additem",{contextmenu:this,el:i.el,index:e}),i.el},removeItem:function(t){var e=this._container;isNaN(t)||(t=e.children[t]),void 0!==t&&(this._removeItem(L.Util.stamp(t)),this._sizeChanged=!0,this._map.fire("contextmenu.removeitem",{contextmenu:this,el:t}))},removeAllItems:function(){for(var t;this._container.children.length;)t=this._container.children[0],this._removeItem(L.Util.stamp(t))},setDisabled:function(t,e){var i=this._container,n=L.Map.ContextMenu.BASE_CLS+"-item";isNaN(t)||(t=i.children[t]),void 0!==t&&L.DomUtil.hasClass(t,n)&&(e?(L.DomUtil.addClass(t,n+"-disabled"),this._map.fire("contextmenu.disableitem",{contextmenu:this,el:t})):(L.DomUtil.removeClass(t,n+"-disabled"),this._map.fire("contextmenu.enableitem",{contextmenu:this,el:t})))},setHidden:function(t,e){var i=this._container,n=L.Map.ContextMenu.BASE_CLS+"-item",s=L.Map.ContextMenu.BASE_CLS+"-separator";isNaN(t)||(t=i.children[t]),void 0!==t&&L.DomUtil.hasClass(t,n)?e?(L.DomUtil.addClass(t,n+"-hidden"),this._map.fire("contextmenu.hideitem",{contextmenu:this,el:t})):(L.DomUtil.removeClass(t,n+"-hidden"),this._map.fire("contextmenu.showitem",{contextmenu:this,el:t})):void 0!==t&&L.DomUtil.hasClass(t,s)&&(e?L.DomUtil.addClass(t,s+"-hidden"):L.DomUtil.removeClass(t,s+"-hidden"))},isVisible:function(){return this._visible},_changeState:function(t){var e,i,n,s,t=void 0!==t?t:this._defaultState;if(t!==this._state)for(n=0,s=this._sets.length;n<s;n++)if(e=this._sets[n],e.state===t||e.name===t&&e.state!==this._state){for(this._map.fire("contextmenu.changestate",{contextmenu:this,set:e,state:t}),n=0,s=this._items.length;n<s;n++)i=this._items[n],this.setHidden(this._items[n].el,i.state.indexOf(e.state)===-1&&i.state.indexOf(e.name)===-1);this._sizeChanged=!0,this._state=t;break}return e},_createSets:function(){var t,e,i=this._map.options.contextmenuSets;for(t=0,e=i.length;t<e;t++)this._sets.push(this._createSet(i[t],this._sets.length))},_createSet:function(t,e){void 0!==t.name?t.name:"set_"+e;return{id:e,name:t.name,state:t.state}},_createItems:function(){var t,e,i=this._map.options.contextmenuItems;for(t=0,e=i.length;t<e;t++)this._items.push(this._createItem(this._container,i[t]))},_createItem:function(t,e,i){if(e.separator||"-"===e)return this._createSeparator(t,i,e.state);var n=L.Map.ContextMenu.BASE_CLS+"-item",s=void 0!==e.state?Array.isArray(e.state)?e.state:[e.state]:[this._defaultState],o=e.disabled?n+" "+n+"-disabled":e.hidden?n+" "+n+"-hidden":n,a=this._insertElementAt("a",o,t,i),h=this._createEventHandler(a,e.callback,e.context,e.hideOnSelect),m="";return e.icon?m='<img class="'+L.Map.ContextMenu.BASE_CLS+'-icon" src="'+e.icon+'"/>':e.iconCls&&(m='<span class="'+L.Map.ContextMenu.BASE_CLS+"- icon "+e.iconCls+'"></span>'),a.innerHTML=m+e.text,a.href="#",L.DomEvent.on(a,"mouseover",this._onItemMouseOver,this).on(a,"mouseout",this._onItemMouseOut,this).on(a,"mousedown",L.DomEvent.stopPropagation).on(a,"click",h),{id:L.Util.stamp(a),el:a,callback:h,state:s}},_removeItem:function(t){var e,i,n,s,o;for(s=0,o=this._items.length;s<o;s++)if(e=this._items[s],e.id===t)return n=e.el,i=e.callback,i&&L.DomEvent.off(n,"mouseover",this._onItemMouseOver,this).off(n,"mouseover",this._onItemMouseOut,this).off(n,"mousedown",L.DomEvent.stopPropagation).off(n,"click",e.callback),this._container.removeChild(n),this._items.splice(s,1),e;return null},_createSeparator:function(t,e,i){var n=this._insertElementAt("div",L.Map.ContextMenu.BASE_CLS+"-separator",t,e),i=void 0!==i?Array.isArray(i)?i:[i]:[this._defaultState];return{id:L.Util.stamp(n),el:n,state:i}},_createEventHandler:function(t,e,i,n){var s=this,o=this._map,a=L.Map.ContextMenu.BASE_CLS+"-item-disabled",n=void 0===n||n;return function(h){L.DomUtil.hasClass(t,a)||(n&&s._hide(),e&&e.call(i||o,s._showLocation),s._map.fire("contextmenu:select",{contextmenu:s,el:t}))}},_insertElementAt:function(t,e,i,n){var s,o=document.createElement(t);return o.className=e,void 0!==n&&(s=i.children[n]),s?i.insertBefore(o,s):i.appendChild(o),o},_show:function(t){this._showAtPoint(t.containerPoint)},_showAtPoint:function(t,e,i){if(this._items.length){var n=this._map,s=n.containerPointToLayerPoint(t),o=n.layerPointToLatLng(s),a={contextmenu:this,state:i},i=void 0!==i?i:this._activeState;e&&(a=L.extend(e,a)),this._showLocation={state:i,target:e?e.relatedTarget:null,latlng:o,layerPoint:s,containerPoint:t},this._setPosition(t),this._changeState(i),this._visible?this._setPosition(t):(this._container.style.display="block",this._visible=!0),this._map.fire("contextmenu.show",a)}},_hide:function(){this._visible&&(this.setState(this._defaultState),this._visible=!1,this._container.style.display="none",this._map.fire("contextmenu.hide",{contextmenu:this}))},_setPosition:function(t){var e,i=this._map.getSize(),n=this._container,s=this._getElementSize(n);this._map.options.contextmenuAnchor&&(e=L.point(this._map.options.contextmenuAnchor),t=t.add(e)),n._leaflet_pos=t,t.x+s.x>i.x?(n.style.left="auto",n.style.right=Math.max(i.x-t.x,0)+"px"):(n.style.left=Math.max(t.x,0)+"px",n.style.right="auto"),t.y+s.y>i.y?(n.style.top="auto",n.style.bottom=Math.max(i.y-t.y,0)+"px"):(n.style.top=Math.max(t.y,0)+"px",n.style.bottom="auto")},_getElementSize:function(t){var e=this._size,i=t.style.display;return e&&!this._sizeChanged||(e={},t.style.left="-999999px",t.style.right="auto",t.style.display="block",e.x=t.offsetWidth,e.y=t.offsetHeight,t.style.left="auto",t.style.display=i,this._sizeChanged=!1),e},_onMouseDown:function(t){this._hide()},_onKeyDown:function(t){var e=t.keyCode;27===e&&this._hide()},_onItemMouseOver:function(t){L.DomUtil.addClass(t.target,"over")},_onItemMouseOut:function(t){L.DomUtil.removeClass(t.target,"over")}}),L.Map.addInitHook("addHandler","contextmenu",L.Map.ContextMenu),L.Mixin.ContextMenu={_initContextMenu:function(){this._items=[],this.on("contextmenu",this._showContextMenu,this)},_showContextMenu:function(t){var e,i,n,s;if(this._map.contextmenu){for(i=this._map.mouseEventToContainerPoint(t.originalEvent),n=0,s=this.options.contextmenuItems.length;n<s;n++)e=this.options.contextmenuItems[n],this._items.push(this._map.contextmenu.insertItem(e,e.index));this._map.once("contextmenu.hide",this._hideContextMenu,this),this._map.contextmenu.showAt(i,{relatedTarget:this},this.options.contextmenuAtiveState)}},_hideContextMenu:function(){var t,e;for(t=0,e=this._items.length;t<e;t++)this._map.contextmenu.removeItem(this._items[t]);this._items.length=0}},L.Marker.mergeOptions({contextmenu:!1,contextmenuItems:[]}),L.Marker.addInitHook(function(){this.options.contextmenu&&this._initContextMenu()}),L.Marker.include(L.Mixin.ContextMenu),L.Path.mergeOptions({contextmenu:!1,contextmenuItems:[]}),L.Path.addInitHook(function(){this.options.contextmenu&&this._initContextMenu()}),L.Path.include(L.Mixin.ContextMenu);
+!function(t){var e;if("function"==typeof define&&define.amd)define(["leaflet"],t);else if("undefined"!=typeof module)e=require("leaflet"),module.exports=t(e);else{if("undefined"==typeof window.L)throw new Error("Leaflet must be loaded first");t(window.L)}}(function(t){t.Map.mergeOptions({contextmenuItems:[]}),t.Map.ContextMenu=t.Handler.extend({_touchstart:t.Browser.msPointer?"MSPointerDown":t.Browser.pointer?"pointerdown":"touchstart",statics:{BASE_CLS:"leaflet-contextmenu"},initialize:function(e){t.Handler.prototype.initialize.call(this,e),this._items=[],this._visible=!1;var n=this._container=t.DomUtil.create("div",t.Map.ContextMenu.BASE_CLS,e._container);n.style.zIndex=1e4,n.style.position="absolute",e.options.contextmenuWidth&&(n.style.width=e.options.contextmenuWidth+"px"),this._createItems(),t.DomEvent.on(n,"click",t.DomEvent.stop).on(n,"mousedown",t.DomEvent.stop).on(n,"dblclick",t.DomEvent.stop).on(n,"contextmenu",t.DomEvent.stop)},addHooks:function(){var e=this._map.getContainer();t.DomEvent.on(e,"mouseleave",this._hide,this).on(document,"keydown",this._onKeyDown,this),t.Browser.touch&&t.DomEvent.on(document,this._touchstart,this._hide,this),this._map.on({contextmenu:this._show,mousedown:this._hide,movestart:this._hide,zoomstart:this._hide},this)},removeHooks:function(){var e=this._map.getContainer();t.DomEvent.off(e,"mouseleave",this._hide,this).off(document,"keydown",this._onKeyDown,this),t.Browser.touch&&t.DomEvent.off(document,this._touchstart,this._hide,this),this._map.off({contextmenu:this._show,mousedown:this._hide,movestart:this._hide,zoomstart:this._hide},this)},showAt:function(e,n){e instanceof t.LatLng&&(e=this._map.latLngToContainerPoint(e)),this._showAtPoint(e,n)},hide:function(){this._hide()},addItem:function(t){return this.insertItem(t)},insertItem:function(t,e){e=void 0!==e?e:this._items.length;var n=this._createItem(this._container,t,e);return this._items.push(n),this._sizeChanged=!0,this._map.fire("contextmenu.additem",{contextmenu:this,el:n.el,index:e}),n.el},removeItem:function(e){var n=this._container;isNaN(e)||(e=n.children[e]),e&&(this._removeItem(t.Util.stamp(e)),this._sizeChanged=!0,this._map.fire("contextmenu.removeitem",{contextmenu:this,el:e}))},removeAllItems:function(){for(var e;this._container.children.length;)e=this._container.children[0],this._removeItem(t.Util.stamp(e))},hideAllItems:function(){var t,e,n;for(e=0,n=this._items.length;e<n;e++)t=this._items[e],t.el.style.display="none"},showAllItems:function(){var t,e,n;for(e=0,n=this._items.length;e<n;e++)t=this._items[e],t.el.style.display=""},setDisabled:function(e,n){var i=this._container,o=t.Map.ContextMenu.BASE_CLS+"-item";isNaN(e)||(e=i.children[e]),e&&t.DomUtil.hasClass(e,o)&&(n?(t.DomUtil.addClass(e,o+"-disabled"),this._map.fire("contextmenu.disableitem",{contextmenu:this,el:e})):(t.DomUtil.removeClass(e,o+"-disabled"),this._map.fire("contextmenu.enableitem",{contextmenu:this,el:e})))},isVisible:function(){return this._visible},_createItems:function(){var t,e,n=this._map.options.contextmenuItems;for(t=0,e=n.length;t<e;t++)this._items.push(this._createItem(this._container,n[t]))},_createItem:function(e,n,i){if(n.separator||"-"===n)return this._createSeparator(e,i);var o=t.Map.ContextMenu.BASE_CLS+"-item",s=n.disabled?o+" "+o+"-disabled":o,h=this._insertElementAt("a",s,e,i),a=this._createEventHandler(h,n.callback,n.context,n.hideOnSelect),m="";return n.icon?m='<img class="'+t.Map.ContextMenu.BASE_CLS+'-icon" src="'+n.icon+'"/>':n.iconCls&&(m='<span class="'+t.Map.ContextMenu.BASE_CLS+"-icon "+n.iconCls+'"></span>'),h.innerHTML=m+n.text,h.href="#",t.DomEvent.on(h,"mouseover",this._onItemMouseOver,this).on(h,"mouseout",this._onItemMouseOut,this).on(h,"mousedown",t.DomEvent.stopPropagation).on(h,"click",a),t.Browser.touch&&t.DomEvent.on(h,this._touchstart,t.DomEvent.stopPropagation),{id:t.Util.stamp(h),el:h,callback:a}},_removeItem:function(e){var n,i,o,s,h;for(o=0,s=this._items.length;o<s;o++)if(n=this._items[o],n.id===e)return i=n.el,h=n.callback,h&&(t.DomEvent.off(i,"mouseover",this._onItemMouseOver,this).off(i,"mouseover",this._onItemMouseOut,this).off(i,"mousedown",t.DomEvent.stopPropagation).off(i,"click",h),t.Browser.touch&&t.DomEvent.off(i,this._touchstart,t.DomEvent.stopPropagation)),this._container.removeChild(i),this._items.splice(o,1),n;return null},_createSeparator:function(e,n){var i=this._insertElementAt("div",t.Map.ContextMenu.BASE_CLS+"-separator",e,n);return{id:t.Util.stamp(i),el:i}},_createEventHandler:function(e,n,i,o){var s=this,h=this._map,a=t.Map.ContextMenu.BASE_CLS+"-item-disabled",o=void 0===o||o;return function(m){t.DomUtil.hasClass(e,a)||(o&&s._hide(),n&&n.call(i||h,s._showLocation),s._map.fire("contextmenu:select",{contextmenu:s,el:e}))}},_insertElementAt:function(t,e,n,i){var o,s=document.createElement(t);return s.className=e,void 0!==i&&(o=n.children[i]),o?n.insertBefore(s,o):n.appendChild(s),s},_show:function(t){this._showAtPoint(t.containerPoint,t)},_showAtPoint:function(e,n){if(this._items.length){var i=this._map,o=i.containerPointToLayerPoint(e),s=i.layerPointToLatLng(o),h=t.extend(n||{},{contextmenu:this});this._showLocation={latlng:s,layerPoint:o,containerPoint:e},n&&n.relatedTarget&&(this._showLocation.relatedTarget=n.relatedTarget),this._setPosition(e),this._visible?this._setPosition(e):(this._container.style.display="block",this._visible=!0),this._map.fire("contextmenu.show",h)}},_hide:function(){this._visible&&(this._visible=!1,this._container.style.display="none",this._map.fire("contextmenu.hide",{contextmenu:this}))},_setPosition:function(e){var n,i=this._map.getSize(),o=this._container,s=this._getElementSize(o);this._map.options.contextmenuAnchor&&(n=t.point(this._map.options.contextmenuAnchor),e=e.add(n)),o._leaflet_pos=e,e.x+s.x>i.x?(o.style.left="auto",o.style.right=Math.max(i.x-e.x,0)+"px"):(o.style.left=Math.max(e.x,0)+"px",o.style.right="auto"),e.y+s.y>i.y?(o.style.top="auto",o.style.bottom=Math.max(i.y-e.y,0)+"px"):(o.style.top=Math.max(e.y,0)+"px",o.style.bottom="auto")},_getElementSize:function(t){var e=this._size,n=t.style.display;return e&&!this._sizeChanged||(e={},t.style.left="-999999px",t.style.right="auto",t.style.display="block",e.x=t.offsetWidth,e.y=t.offsetHeight,t.style.left="auto",t.style.display=n,this._sizeChanged=!1),e},_onKeyDown:function(t){var e=t.keyCode;27===e&&this._hide()},_onItemMouseOver:function(e){t.DomUtil.addClass(e.target||e.srcElement,"over")},_onItemMouseOut:function(e){t.DomUtil.removeClass(e.target||e.srcElement,"over")}}),t.Map.addInitHook("addHandler","contextmenu",t.Map.ContextMenu),t.Mixin.ContextMenu={bindContextMenu:function(e){return t.setOptions(this,e),this._initContextMenu(),this},unbindContextMenu:function(){return this.off("contextmenu",this._showContextMenu,this),this},addContextMenuItem:function(t){this.options.contextmenuItems.push(t)},removeContextMenuItemWithIndex:function(t){for(var e=[],n=0;n<this.options.contextmenuItems.length;n++)this.options.contextmenuItems[n].index==t&&e.push(n);for(var i=e.pop();void 0!==i;)this.options.contextmenuItems.splice(i,1),i=e.pop()},replaceConextMenuItem:function(t){this.removeContextMenuItemWithIndex(t.index),this.addContextMenuItem(t)},_initContextMenu:function(){this._items=[],this.on("contextmenu",this._showContextMenu,this)},_showContextMenu:function(e){var n,i,o,s,h;if(this._map.contextmenu){for(i=t.extend({relatedTarget:this},e),o=this._map.mouseEventToContainerPoint(e.originalEvent),this.options.contextmenuInheritItems||this._map.contextmenu.hideAllItems(),s=0,h=this.options.contextmenuItems.length;s<h;s++)n=this.options.contextmenuItems[s],this._items.push(this._map.contextmenu.insertItem(n,n.index));this._map.once("contextmenu.hide",this._hideContextMenu,this),this._map.contextmenu.showAt(o,i)}},_hideContextMenu:function(){var t,e;for(t=0,e=this._items.length;t<e;t++)this._map.contextmenu.removeItem(this._items[t]);this._items.length=0,this.options.contextmenuInheritItems||this._map.contextmenu.showAllItems()}};var e,n,i,o=[t.Marker,t.Path],s={contextmenu:!1,contextmenuItems:[],contextmenuInheritItems:!0};for(n=0,i=o.length;n<i;n++)e=o[n],e.prototype.options?e.mergeOptions(s):e.prototype.options=s,e.addInitHook(function(){this.options.contextmenu&&this._initContextMenu()}),e.include(t.Mixin.ContextMenu);return t.Map.ContextMenu});
 
-},{}],18:[function(require,module,exports){
+},{"leaflet":4}],18:[function(require,module,exports){
 L.Control.Elevation=L.Control.extend({options:{position:"topright",theme:"lime-theme",width:600,height:175,margins:{top:10,right:20,bottom:30,left:60},useHeightIndicator:!0,interpolation:"linear",hoverNumber:{decimalsX:3,decimalsY:0,formatter:void 0},xTicks:void 0,yTicks:void 0,collapsed:!1,yAxisMin:void 0,yAxisMax:void 0,forceAxisBounds:!1,controlButton:{iconCssClass:"elevation-toggle-icon",title:"Elevation"},imperial:!1},__mileFactor:.621371,__footFactor:3.28084,onRemove:function(t){this._container=null},onAdd:function(t){this._map=t;var i=this.options,e=i.margins;i.xTicks=i.xTicks||Math.round(this._width()/75),i.yTicks=i.yTicks||Math.round(this._height()/30),i.hoverNumber.formatter=i.hoverNumber.formatter||this._formatter;var a=this._x=d3.scale.linear().range([0,this._width()]),s=this._y=d3.scale.linear().range([this._height(),0]),o=(this._area=d3.svg.area().interpolate(i.interpolation).x(function(t){var i=a(t.dist);return t.xDiagCoord=i,i}).y0(this._height()).y1(function(t){return s(t.altitude)}),this._container=L.DomUtil.create("div","elevation"));L.DomUtil.addClass(o,i.theme),this._initToggle();var r=d3.select(o);r.attr("width",i.width);var n=r.append("svg");n.attr("width",i.width).attr("class","background").attr("height",i.height).append("g").attr("transform","translate("+e.left+","+e.top+")");var h=d3.svg.line();h=h.x(function(t){return d3.mouse(n.select("g"))[0]}).y(function(t){return this._height()});var l=d3.select(this._container).select("svg").select("g");this._areapath=l.append("path").attr("class","area");var d=this._background=l.append("rect").attr("width",this._width()).attr("height",this._height()).style("fill","none").style("stroke","none").style("pointer-events","all");L.Browser.touch?(d.on("touchmove.drag",this._dragHandler.bind(this)).on("touchstart.drag",this._dragStartHandler.bind(this)).on("touchstart.focus",this._mousemoveHandler.bind(this)),L.DomEvent.on(this._container,"touchend",this._dragEndHandler,this)):(d.on("mousemove.focus",this._mousemoveHandler.bind(this)).on("mouseout.focus",this._mouseoutHandler.bind(this)).on("mousedown.drag",this._dragStartHandler.bind(this)).on("mousemove.drag",this._dragHandler.bind(this)),L.DomEvent.on(this._container,"mouseup",this._dragEndHandler,this)),this._xaxisgraphicnode=l.append("g"),this._yaxisgraphicnode=l.append("g"),this._appendXaxis(this._xaxisgraphicnode),this._appendYaxis(this._yaxisgraphicnode);var c=this._focusG=l.append("g");return this._mousefocus=c.append("svg:line").attr("class","mouse-focus-line").attr("x2","0").attr("y2","0").attr("x1","0").attr("y1","0"),this._focuslabelX=c.append("svg:text").style("pointer-events","none").attr("class","mouse-focus-label-x"),this._focuslabelY=c.append("svg:text").style("pointer-events","none").attr("class","mouse-focus-label-y"),this._data&&this._applyData(),o},_dragHandler:function(){d3.event.preventDefault(),d3.event.stopPropagation(),this._gotDragged=!0,this._drawDragRectangle()},_drawDragRectangle:function(){if(this._dragStartCoords){var t=this._dragCurrentCoords=d3.mouse(this._background.node()),i=Math.min(this._dragStartCoords[0],t[0]),e=Math.max(this._dragStartCoords[0],t[0]);if(this._dragRectangle||this._dragRectangleG)this._dragRectangle.attr("width",e-i).attr("x",i);else{var a=d3.select(this._container).select("svg").select("g");this._dragRectangleG=a.append("g"),this._dragRectangle=this._dragRectangleG.append("rect").attr("width",e-i).attr("height",this._height()).attr("x",i).attr("class","mouse-drag").style("pointer-events","none")}}},_resetDrag:function(){this._dragRectangleG&&(this._dragRectangleG.remove(),this._dragRectangleG=null,this._dragRectangle=null,this._hidePositionMarker(),this._map.fitBounds(this._fullExtent))},_dragEndHandler:function(){if(!this._dragStartCoords||!this._gotDragged)return this._dragStartCoords=null,this._gotDragged=!1,void this._resetDrag();this._hidePositionMarker();var t=this._findItemForX(this._dragStartCoords[0]),i=this._findItemForX(this._dragCurrentCoords[0]);this._fitSection(t,i),this._dragStartCoords=null,this._gotDragged=!1},_dragStartHandler:function(){d3.event.preventDefault(),d3.event.stopPropagation(),this._gotDragged=!1,this._dragStartCoords=d3.mouse(this._background.node())},_findItemForX:function(t){var i=d3.bisector(function(t){return t.dist}).left,e=this._x.invert(t);return i(this._data,e)},_findItemForLatLng:function(t){var i=null,e=1/0;return this._data.forEach(function(a){var s=t.distanceTo(a.latlng);e>s&&(e=s,i=a)}),i},_fitSection:function(t,i){var e=Math.min(t,i),a=Math.max(t,i),s=this._calculateFullExtent(this._data.slice(e,a));this._map.fitBounds(s)},_initToggle:function(){var t=this._container;if(t.setAttribute("aria-haspopup",!0),L.Browser.touch?L.DomEvent.on(t,"click",L.DomEvent.stopPropagation):L.DomEvent.disableClickPropagation(t),this.options.collapsed){this._collapse(),L.Browser.android||L.DomEvent.on(t,"mouseover",this._expand,this).on(t,"mouseout",this._collapse,this);var i=this._button=L.DomUtil.create("a","elevation-toggle "+this.options.controlButton.iconCssClass,t);i.href="#",i.title=this.options.controlButton.title,L.Browser.touch?L.DomEvent.on(i,"click",L.DomEvent.stop).on(i,"click",this._expand,this):L.DomEvent.on(i,"focus",this._expand,this),this._map.on("click",this._collapse,this)}},_expand:function(){this._container.className=this._container.className.replace(" elevation-collapsed","")},_collapse:function(){L.DomUtil.addClass(this._container,"elevation-collapsed")},_width:function(){var t=this.options;return t.width-t.margins.left-t.margins.right},_height:function(){var t=this.options;return t.height-t.margins.top-t.margins.bottom},_formatter:function(t,i,e){var a;a=0===i?Math.round(t)+"":L.Util.formatNum(t,i)+"";var s=a.split(".");if(s[1]){for(var o=i-s[1].length;o>0;o--)s[1]+="0";a=s.join(e||".")}return a},_appendYaxis:function(t){var i=this.options;i.imperial?t.attr("class","y axis").call(d3.svg.axis().scale(this._y).ticks(this.options.yTicks).orient("left")).append("text").attr("x",-37).attr("y",3).style("text-anchor","end").text("ft"):t.attr("class","y axis").call(d3.svg.axis().scale(this._y).ticks(this.options.yTicks).orient("left")).append("text").attr("x",-45).attr("y",3).style("text-anchor","end").text("m")},_appendXaxis:function(t){var i=this.options;i.imperial?t.attr("class","x axis").attr("transform","translate(0,"+this._height()+")").call(d3.svg.axis().scale(this._x).ticks(this.options.xTicks).orient("bottom")).append("text").attr("x",this._width()+10).attr("y",15).style("text-anchor","end").text("mi"):t.attr("class","x axis").attr("transform","translate(0,"+this._height()+")").call(d3.svg.axis().scale(this._x).ticks(this.options.xTicks).orient("bottom")).append("text").attr("x",this._width()+20).attr("y",15).style("text-anchor","end").text("km")},_updateAxis:function(){this._xaxisgraphicnode.selectAll("g").remove(),this._xaxisgraphicnode.selectAll("path").remove(),this._xaxisgraphicnode.selectAll("text").remove(),this._yaxisgraphicnode.selectAll("g").remove(),this._yaxisgraphicnode.selectAll("path").remove(),this._yaxisgraphicnode.selectAll("text").remove(),this._appendXaxis(this._xaxisgraphicnode),this._appendYaxis(this._yaxisgraphicnode)},_mouseoutHandler:function(){this._hidePositionMarker()},_hidePositionMarker:function(){this._marker&&(this._map.removeLayer(this._marker),this._marker=null),this._mouseHeightFocus&&(this._mouseHeightFocus.style("visibility","hidden"),this._mouseHeightFocusLabel.style("visibility","hidden")),this._pointG&&this._pointG.style("visibility","hidden"),this._focusG.style("visibility","hidden")},_mousemoveHandler:function(t,i,e){if(this._data&&0!==this._data.length){var a=d3.mouse(this._background.node()),s=this.options,o=this._data[this._findItemForX(a[0])],r=o.altitude,n=o.dist,h=o.latlng,l=s.hoverNumber.formatter(r,s.hoverNumber.decimalsY);s.hoverNumber.formatter(n,s.hoverNumber.decimalsX),this._showDiagramIndicator(o,a[0]);var d=this._map.latLngToLayerPoint(h);if(s.useHeightIndicator){if(!this._mouseHeightFocus){var c=d3.select(".leaflet-overlay-pane svg").append("g");this._mouseHeightFocus=c.append("svg:line").attr("class",s.theme+" height-focus line").attr("x2",0).attr("y2",0).attr("x1",0).attr("y1",0);var _=this._pointG=c.append("g");_.append("svg:circle").attr("r",6).attr("cx",0).attr("cy",0).attr("class",s.theme+" height-focus circle-lower"),this._mouseHeightFocusLabel=c.append("svg:text").attr("class",s.theme+" height-focus-label").style("pointer-events","none")}var u=this._height()/this._maxElevation*r,g=d.y-u;this._mouseHeightFocus.attr("x1",d.x).attr("x2",d.x).attr("y1",d.y).attr("y2",g).style("visibility","visible"),this._pointG.attr("transform","translate("+d.x+","+d.y+")").style("visibility","visible"),s.imperial?this._mouseHeightFocusLabel.attr("x",d.x).attr("y",g).text(l+" ft").style("visibility","visible"):this._mouseHeightFocusLabel.attr("x",d.x).attr("y",g).text(l+" m").style("visibility","visible")}else this._marker?this._marker.setLatLng(h):this._marker=new L.Marker(h).addTo(this._map)}},_addGeoJSONData:function(t){var i=this.options;if(t){for(var e=this._data||[],a=this._dist||0,s=this._maxElevation||0,o=0;o<t.length;o++){var r=new L.LatLng(t[o][1],t[o][0]),n=new L.LatLng(t[o?o-1:0][1],t[o?o-1:0][0]),h=i.imperial?r.distanceTo(n)*this.__mileFactor:r.distanceTo(n);a+=Math.round(h/1e3*1e5)/1e5,s=s<t[o][2]?t[o][2]:s,e.push({dist:a,altitude:i.imperial?t[o][2]*this.__footFactor:t[o][2],x:t[o][0],y:t[o][1],latlng:r})}this._dist=a,this._data=e,s=i.imperial?s*this.__footFactor:s,this._maxElevation=s}},_addGPXdata:function(t){var i=this.options;if(t){for(var e=this._data||[],a=this._dist||0,s=this._maxElevation||0,o=0;o<t.length;o++){var r=t[o],n=t[o?o-1:0],h=i.imperial?r.distanceTo(n)*this.__mileFactor:r.distanceTo(n);a+=Math.round(h/1e3*1e5)/1e5,s=s<r.meta.ele?r.meta.ele:s,e.push({dist:a,altitude:i.imperial?r.meta.ele*this.__footFactor:r.meta.ele,x:r.lng,y:r.lat,latlng:r})}this._dist=a,this._data=e,s=i.imperial?s*this.__footFactor:s,this._maxElevation=s}},_addData:function(t){var i,e=t&&t.geometry&&t.geometry;if(e)switch(e.type){case"LineString":this._addGeoJSONData(e.coordinates);break;case"MultiLineString":for(i=0;i<e.coordinates.length;i++)this._addGeoJSONData(e.coordinates[i]);break;default:throw new Error("Invalid GeoJSON object.")}var a=t&&"FeatureCollection"===t.type;if(a)for(i=0;i<t.features.length;i++)this._addData(t.features[i]);t&&t._latlngs&&this._addGPXdata(t._latlngs)},_calculateFullExtent:function(t){if(!t||t.length<1)throw new Error("no data in parameters");var i=new L.latLngBounds(t[0].latlng,t[0].latlng);return t.forEach(function(t){i.extend(t.latlng)}),i},addData:function(t,i){this._addData(t),this._container&&this._applyData(),null===i&&t.on&&(i=t),i&&i.on("mousemove",this._handleLayerMouseOver.bind(this))},_handleLayerMouseOver:function(t){if(this._data&&0!==this._data.length){var i=t.latlng,e=this._findItemForLatLng(i);if(e){var a=e.xDiagCoord;this._showDiagramIndicator(e,a)}}},_showDiagramIndicator:function(t,i){var e=this.options;this._focusG.style("visibility","visible"),this._mousefocus.attr("x1",i).attr("y1",0).attr("x2",i).attr("y2",this._height()).classed("hidden",!1);var a=t.altitude,s=t.dist,o=(t.latlng,e.hoverNumber.formatter(a,e.hoverNumber.decimalsY)),r=e.hoverNumber.formatter(s,e.hoverNumber.decimalsX);e.imperial?(this._focuslabelX.attr("x",i).text(o+" ft"),this._focuslabelY.attr("y",this._height()-5).attr("x",i).text(r+" mi")):(this._focuslabelX.attr("x",i).text(o+" m"),this._focuslabelY.attr("y",this._height()-5).attr("x",i).text(r+" km"))},_applyData:function(){var t=d3.extent(this._data,function(t){return t.dist}),i=d3.extent(this._data,function(t){return t.altitude}),e=this.options;void 0!==e.yAxisMin&&(e.yAxisMin<i[0]||e.forceAxisBounds)&&(i[0]=e.yAxisMin),void 0!==e.yAxisMax&&(e.yAxisMax>i[1]||e.forceAxisBounds)&&(i[1]=e.yAxisMax),this._x.domain(t),this._y.domain(i),this._areapath.datum(this._data).attr("d",this._area),this._updateAxis(),this._fullExtent=this._calculateFullExtent(this._data)},_clearData:function(){this._data=null,this._dist=null,this._maxElevation=null},clear:function(){this._clearData(),this._areapath&&(this._areapath.attr("d","M0 0"),this._x.domain([0,1]),this._y.domain([0,1]),this._updateAxis())},hide:function(){this._container.style.display="none"},show:function(){this._container.style.display="block"}}),L.control.elevation=function(t){return new L.Control.Elevation(t)};
 
 },{}],19:[function(require,module,exports){
@@ -66,10 +66,10 @@ L.NumberedDivIcon=L.Icon.extend({options:{iconUrl:"./img/marker_hole.png",number
 
 },{}],20:[function(require,module,exports){
 (function (global){
-function initFromParams(e,t){ghRequest.init(e);var o,r=0;if(e.point)for(var a=0;a<e.point.length;a++)""!==e.point[a]&&(r++,o=a);var n=e.point&&r>=2;n?resolveCoords(e.point,t):e.point&&1===r&&(ghRequest.route.set(e.point[o],o,!0),resolveIndex(o).done(function(){mapLayer.focus(ghRequest.route.getIndex(o),15,o)}))}function resolveCoords(e,t){for(var o=0,r=e.length;o<r;o++){var a=e[o],n=ghRequest.route.getIndex(o);n&&a===n.input&&n.isResolved()||ghRequest.route.set(a,o,!0)}checkInput(),ghRequest.route.isResolved()?(resolveAll(),routeLatLng(ghRequest,t)):$.when.apply($,resolveAll()).done(function(){routeLatLng(ghRequest,t)})}function getToFrom(e){return 0===e?FROM:e===ghRequest.route.size()-1?TO:-1}function checkInput(){var e=$("#pointTemplate").html(),t=ghRequest.route.size();$("#locationpoints > div.pointDiv").length>t&&$("#locationpoints > div.pointDiv:gt("+(t-1)+")").remove(),$("#locationpoints .pointDelete").off();for(var o=function(){var e=$(this).parent().data("index");ghRequest.route.removeSingle(e),mapLayer.clearLayers(),routeLatLng(ghRequest,!1)},r=0;r<t;r++){var a=$("#locationpoints > div.pointDiv").eq(r);0===a.length&&($("#locationpoints > div.pointAdd").before(translate.nanoTemplate(e,{id:r})),a=$("#locationpoints > div.pointDiv").eq(r));var n=getToFrom(r);if(a.data("index",r),a.find(".pointFlag").attr("src",n===FROM?"img/marker-small-green.png":n===TO?"img/marker-small-red.png":"img/marker-small-blue.png"),t>2?a.find(".pointDelete").click(o).prop("disabled",!1).removeClass("ui-state-disabled"):a.find(".pointDelete").prop("disabled",!0).addClass("ui-state-disabled"),autocomplete.showListForIndex(ghRequest,routeIfAllResolved,r),translate.isI18nIsInitialized()){var s=a.find(".pointInput");0===r?$(s).attr("placeholder",translate.tr("from_hint")):r===t-1?$(s).attr("placeholder",translate.tr("to_hint")):$(s).attr("placeholder",translate.tr("via_hint"))}}}function setToStart(e){var t=e.target.getLatLng(),o=ghRequest.route.getIndexByCoord(t);ghRequest.route.move(o,0),routeIfAllResolved()}function setToEnd(e){var t=e.target.getLatLng(),o=ghRequest.route.getIndexByCoord(t);ghRequest.route.move(o,-1),routeIfAllResolved()}function setStartCoord(e){ghRequest.route.set(e.latlng.wrap(),0),resolveFrom(),routeIfAllResolved()}function setIntermediateCoord(e){var t=mapLayer.getSubLayers("route"),o=t.map(function(e){return{coordinates:e.getLatLngs(),wayPoints:e.feature.properties.snapped_waypoints.coordinates.map(function(e){return L.latLng(e[1],e[0])})}}),r=routeManipulation.getIntermediatePointIndex(o,e.latlng);ghRequest.route.add(e.latlng.wrap(),r),resolveIndex(r),routeIfAllResolved()}function deleteCoord(e){var t=e.target.getLatLng();ghRequest.route.removeSingle(t),mapLayer.clearLayers(),routeLatLng(ghRequest,!1)}function setEndCoord(e){var t=ghRequest.route.size()-1;ghRequest.route.set(e.latlng.wrap(),t),resolveTo(),routeIfAllResolved()}function routeIfAllResolved(e){return!!ghRequest.route.isResolved()&&(routeLatLng(ghRequest,e),!0)}function setFlag(e,t){if(e.lat){var o=getToFrom(t),r=mapLayer.createMarker(t,e,setToEnd,setToStart,deleteCoord,ghRequest);r._openPopup=r.openPopup,r.openPopup=function(){var e,t=this.getLatLng(),o=ghRequest.route.getIndexFromCoord(t);if(o.resolvedList&&o.resolvedList[0]&&o.resolvedList[0].locationDetails){var r=o.resolvedList[0].locationDetails;e=format.formatAddress(r),this._popup.setContent(e).update()}this._openPopup()};var a={text:translate.tr("set_start"),callback:setToStart,index:1,state:2};o===-1&&r.options.contextmenuItems.push(a),r.on("dragend",function(e){mapLayer.clearLayers();var o=e.target.getLatLng();autocomplete.hide(),ghRequest.route.getIndex(t).setCoord(o.lat,o.lng),resolveIndex(t),ghRequest.do_zoom=!1,routeLatLng(ghRequest,!1)})}}function resolveFrom(){return resolveIndex(0)}function resolveTo(){return resolveIndex(ghRequest.route.size()-1)}function resolveIndex(e){return setFlag(ghRequest.route.getIndex(e),e),0===e?ghRequest.to.isResolved()?mapLayer.setDisabledForMapsContextMenu("start",!1):mapLayer.setDisabledForMapsContextMenu("start",!0):e===ghRequest.route.size()-1&&(ghRequest.from.isResolved()?mapLayer.setDisabledForMapsContextMenu("end",!1):mapLayer.setDisabledForMapsContextMenu("end",!0)),nominatim.resolve(e,ghRequest.route.getIndex(e))}function resolveAll(){for(var e=[],t=0,o=ghRequest.route.size();t<o;t++)e[t]=resolveIndex(t);return e}function flagAll(){for(var e=0,t=ghRequest.route.size();e<t;e++)setFlag(ghRequest.route.getIndex(e),e)}function routeLatLng(e,t){var o=e.do_zoom;e.do_zoom=!0;var r=e.createHistoryURL()+"&layer="+tileLayers.activeLayerName;if(!t&&History.enabled){var a=urlTools.parseUrl(r);return console.log(a),a.do_zoom=o,a.mathRandom=Math.random(),void History.pushState(a,messages.browserTitle,r)}var n=$("#info");n.empty(),n.show();var s=$("<div class='route_results'/>");n.append(s),mapLayer.clearElevation(),mapLayer.clearLayers(),flagAll(),mapLayer.setDisabledForMapsContextMenu("intermediate",!1),$("#vehicles button").removeClass("selectvehicle"),$("button#"+e.getVehicle().toLowerCase()).addClass("selectvehicle");var i=e.createURL();s.html('<img src="img/indicator.gif"/> Search Route ...'),e.doRequest(i,function(t){function a(e,t,o,r,a,n){return function(){var i=e[t];mapLayer.eachLayer(function(e){if(e.setStyle){var t=e.feature===i;e.setStyle(t?d:c),t&&(L.Browser.ie||L.Browser.opera||e.bringToFront())}}),a&&(mapLayer.clearElevation(),mapLayer.addElevation(i,n)),l.find("li").removeClass("current"),s.find("div").removeClass("current"),o.addClass("current"),r.addClass("current")}}if(s.html(""),t.message){var n=t.message;if(console.log(n),t.hints)for(var i=0;i<t.hints.length;i++)s.append("<div class='error'>"+t.hints[i].message+"</div>");else s.append("<div class='error'>"+n+"</div>")}else{var l=$("<ul id='route_result_tabs'/>");t.paths.length>1&&(s.append(l),s.append("<div class='clear'/>"));for(var u,p={color:"#00cc33",weight:5,opacity:.6},d={color:"#00cc33",weight:6,opacity:.8},c={color:"darkgray",weight:6,opacity:.8},g=[],h=0;h<t.paths.length;h++){var v=$("<li>").append(h+1+"<img class='alt_route_img' src='img/alt_route.png'/>");0===h&&(u=v),l.append(v);var m=t.paths[h],f=0===h?p:c,q={type:"Feature",geometry:m.points,properties:{style:f,name:"route",snapped_waypoints:m.snapped_waypoints}};g.push(q),mapLayer.addDataToRoutingLayer(q);var y=$("<div class='route_result_tab'>");s.append(y),v.click(a(g,h,v,y,e.hasElevation(),e.useMiles));var b=translate.createTimeString(m.time),R=translate.createDistanceString(m.distance,e.useMiles),x=$("<div class='route_description'>");m.description&&m.description.length>0&&(x.text(m.description),x.append("<br/>")),x.append(translate.tr("route_info",[R,b]));var I=function(e){return function(){mapLayer.updateScale(e),ghRequest.useMiles=e,resolveAll(),routeLatLng(ghRequest)}},_=$("<button class='plain_text_button "+(e.useMiles?"gray":"")+"'>");_.text(translate.tr2("km_abbr")),_.click(I(!1));var w=$("<button class='plain_text_button "+(e.useMiles?"":"gray")+"'>");w.text(translate.tr2("mi_abbr")),w.click(I(!0));var C=$("<span style='float: right;'>");if(C.append(_),C.append("|"),C.append(w),x.append(C),e.hasElevation()&&x.append(translate.createEleInfoString(m.ascend,m.descend,e.useMiles)),y.append(x),m.instructions){var M=require("./instructions.js");y.append(M.create(mapLayer,m,r,e))}}u.click(),mapLayer.adjustMapSize();var j=t.paths[0];if(j.bbox&&o){var F=j.bbox[0],k=j.bbox[1],S=j.bbox[2],T=j.bbox[3],A=new L.LatLngBounds(new L.LatLng(k,F),new L.LatLng(T,S));mapLayer.fitMapToBounds(A)}$(".defaulting").each(function(e,t){$(t).css("color","black")})}})}function mySubmit(){var e,t,o,r=[],a=!0,n=$("#locationpoints > div.pointDiv > input.pointInput"),s=n.size;if($.each(n,function(n){0===n?(e=$(this).val(),e!==translate.tr("from_hint")&&""!==e?r.push(e):a=!1):n===s-1?(t=$(this).val(),t!==translate.tr("to_hint")&&""!==t?r.push(t):a=!1):(o=$(this).val(),o!==translate.tr("via_hint")&&""!==o?r.push(o):a=!1)}),a&&e!==translate.tr("from_hint"))return t===translate.tr("to_hint")?(ghRequest.from.setStr(e),void $.when(resolveFrom()).done(function(){mapLayer.focus(ghRequest.from,null,0)})):void(a&&resolveCoords(r))}function isProduction(){return host.indexOf("graphhopper.com")>0}global.d3=require("d3");var L=require("leaflet");require("leaflet-loading"),require("./lib/leaflet.contextmenu.js"),require("./lib/leaflet.elevation-0.0.4.min.js"),require("./lib/leaflet_numbered_markers.js"),global.jQuery=require("jquery"),global.$=global.jQuery,require("./lib/jquery-ui-custom-1.12.0.min.js"),require("./lib/jquery.history.js"),require("./lib/jquery.autocomplete.js");var ghenv=require("./config/options.js").options;console.log(ghenv.environment);var GHInput=require("./graphhopper/GHInput.js"),GHRequest=require("./graphhopper/GHRequest.js"),host=ghenv.routing.host;host||(host=""===location.port?location.protocol+"//"+location.hostname:location.protocol+"//"+location.hostname+":"+location.port);var AutoComplete=require("./autocomplete.js");if("development"===ghenv.environment)var autocomplete=AutoComplete.prototype.createStub();else var autocomplete=new AutoComplete(ghenv.geocoding.host,ghenv.geocoding.api_key);var mapLayer=require("./map.js"),nominatim=require("./nominatim.js"),routeManipulation=require("./routeManipulation.js"),gpxExport=require("./gpxexport.js"),messages=require("./messages.js"),translate=require("./translate.js"),format=require("./tools/format.js"),urlTools=require("./tools/url.js"),vehicle=require("./tools/vehicle.js"),tileLayers=require("./config/tileLayers.js"),debug=!1,ghRequest=new GHRequest(host,ghenv.routing.api_key),bounds={},metaVersionInfo;global.window&&(window.log=function(){log.history=log.history||[],log.history.push(arguments),this.console&&debug&&console.log(Array.prototype.slice.call(arguments))}),$(document).ready(function(e){jQuery.support.cors=!0,gpxExport.addGpxExport(ghRequest),isProduction()&&$("#hosting").show();var t=window.History;t.enabled&&t.Adapter.bind(window,"statechange",function(){var e=t.getState();console.log(e),initFromParams(e.data,!0)}),$("#locationform").submit(function(e){e.preventDefault(),mySubmit()});var o=urlTools.parseUrlWithHisto();$.when(ghRequest.fetchTranslationMap(o.locale),ghRequest.getInfo()).then(function(e,t){function r(e,t){var o=$("<button class='vehicle-btn' title='"+translate.tr(e)+"'/>");return t&&o.hide(),o.attr("id",e),o.html("<img src='img/"+e+".png' alt='"+translate.tr(e)+"'></img>"),o.click(function(){ghRequest.initVehicle(e),resolveAll(),routeLatLng(ghRequest)}),o}var a=e[0];autocomplete.setLocale(a.locale),ghRequest.setLocale(a.locale),translate.init(a);var n=t[0],s=n.bbox;bounds.initialized=!0,bounds.minLon=s[0],bounds.minLat=s[1],bounds.maxLon=s[2],bounds.maxLat=s[3],nominatim.setBounds(bounds);var i=$("#vehicles");if(n.features){ghRequest.features=n.features;var l={car:1,foot:2,bike:3,motorcycle:1e4},u=o.vehicle&&(!l[o.vehicle]||l[o.vehicle]>3),p=vehicle.getSortedVehicleKeys(n.features,l);p.length>0&&ghRequest.initVehicle(p[0]);var d=[];for(var c in p){var g=r(p[c].toLowerCase(),!u&&c>2);i.append(g),c>2&&d.push(g)}if(!u&&p.length>3){var h=$("<a id='more-vehicle-btn'> ...</a>").click(function(){h.hide();for(var e in d)d[e].show()});i.append($("<a class='vehicle-info-link' href='https://graphhopper.com/api/1/docs/supported-vehicle-profiles/'>?</a>")),i.append(h)}}metaVersionInfo=messages.extractMetaVersionInfo(n),mapLayer.initMap(bounds,setStartCoord,setIntermediateCoord,setEndCoord,o.layer,o.use_miles),initFromParams(o,!0),checkInput()},function(e){console.log(e),$("#error").html('GraphHopper API offline? <a href="http://graphhopper.com/maps">Refresh</a><br/>Status: '+e.statusText+"<br/>"+host),bounds={minLon:-180,minLat:-90,maxLon:180,maxLat:90},nominatim.setBounds(bounds),mapLayer.initMap(bounds,setStartCoord,setIntermediateCoord,setEndCoord,o.layer,o.use_miles)}),$(window).resize(function(){mapLayer.adjustMapSize()}),$("#locationpoints").sortable({items:".pointDiv",cursor:"n-resize",containment:"parent",handle:".pointFlag",update:function(e,t){var o=$(t.item[0]).data("index");sortable_items=$("#locationpoints > div.pointDiv"),$(sortable_items).each(function(e){var t=$(this).data("index");if(o===t)return ghRequest.route.move(t,e),routeIfAllResolved()||checkInput(),!1})}}),$("#locationpoints > div.pointAdd").click(function(){ghRequest.route.add(new GHInput),checkInput()}),checkInput()});var FROM="from",TO="to";module.exports.setFlag=setFlag;
+function initFromParams(e,t){ghRequest.init(e);var o,r=0;if(e.point)for(var a=0;a<e.point.length;a++)""!==e.point[a]&&(r++,o=a);var n=e.point&&r>=2;n?resolveCoords(e.point,t):e.point&&1===r&&(ghRequest.route.set(e.point[o],o,!0),resolveIndex(o).done(function(){mapLayer.focus(ghRequest.route.getIndex(o),15,o)}))}function resolveCoords(e,t){for(var o=0,r=e.length;o<r;o++){var a=e[o],n=ghRequest.route.getIndex(o);n&&a===n.input&&n.isResolved()||ghRequest.route.set(a,o,!0)}checkInput(),ghRequest.route.isResolved()?(resolveAll(),routeLatLng(ghRequest,t)):$.when.apply($,resolveAll()).done(function(){routeLatLng(ghRequest,t)})}function getToFrom(e){return 0===e?FROM:e===ghRequest.route.size()-1?TO:-1}function checkInput(){var e=$("#pointTemplate").html(),t=ghRequest.route.size();$("#locationpoints > div.pointDiv").length>t&&$("#locationpoints > div.pointDiv:gt("+(t-1)+")").remove(),$("#locationpoints .pointDelete").off();for(var o=function(){var e=$(this).parent().data("index");ghRequest.route.removeSingle(e),mapLayer.clearLayers(),routeLatLng(ghRequest,!1)},r=0;r<t;r++){var a=$("#locationpoints > div.pointDiv").eq(r);0===a.length&&($("#locationpoints > div.pointAdd").before(translate.nanoTemplate(e,{id:r})),a=$("#locationpoints > div.pointDiv").eq(r));var n=getToFrom(r);if(a.data("index",r),a.find(".pointFlag").attr("src",n===FROM?"img/marker-small-green.png":n===TO?"img/marker-small-red.png":"img/marker-small-blue.png"),t>2?a.find(".pointDelete").click(o).prop("disabled",!1).removeClass("ui-state-disabled"):a.find(".pointDelete").prop("disabled",!0).addClass("ui-state-disabled"),autocomplete.showListForIndex(ghRequest,routeIfAllResolved,r),translate.isI18nIsInitialized()){var s=a.find(".pointInput");0===r?$(s).attr("placeholder",translate.tr("from_hint")):r===t-1?$(s).attr("placeholder",translate.tr("to_hint")):$(s).attr("placeholder",translate.tr("via_hint"))}}}function setToStart(e){var t=e.relatedTarget.getLatLng(),o=ghRequest.route.getIndexByCoord(t);ghRequest.route.move(o,0),routeIfAllResolved()}function setToEnd(e){var t=e.relatedTarget.getLatLng(),o=ghRequest.route.getIndexByCoord(t);ghRequest.route.move(o,-1),routeIfAllResolved()}function setStartCoord(e){ghRequest.route.set(e.latlng.wrap(),0),resolveFrom(),routeIfAllResolved()}function setIntermediateCoord(e){var t=mapLayer.getSubLayers("route"),o=t.map(function(e){return{coordinates:e.getLatLngs(),wayPoints:e.feature.properties.snapped_waypoints.coordinates.map(function(e){return L.latLng(e[1],e[0])})}}),r=routeManipulation.getIntermediatePointIndex(o,e.latlng);ghRequest.route.add(e.latlng.wrap(),r),resolveIndex(r),routeIfAllResolved()}function deleteCoord(e){var t=e.relatedTarget.getLatLng();ghRequest.route.removeSingle(t),mapLayer.clearLayers(),routeLatLng(ghRequest,!1)}function setEndCoord(e){var t=ghRequest.route.size()-1;ghRequest.route.set(e.latlng.wrap(),t),resolveTo(),routeIfAllResolved()}function routeIfAllResolved(e){return!!ghRequest.route.isResolved()&&(routeLatLng(ghRequest,e),!0)}function setFlag(e,t){if(e.lat){var o=getToFrom(t),r=mapLayer.createMarker(t,e,setToEnd,setToStart,deleteCoord,ghRequest);r._openPopup=r.openPopup,r.openPopup=function(){var e,t=this.getLatLng(),o=ghRequest.route.getIndexFromCoord(t);if(o.resolvedList&&o.resolvedList[0]&&o.resolvedList[0].locationDetails){var r=o.resolvedList[0].locationDetails;e=format.formatAddress(r),this._popup.setContent(e).update()}this._openPopup()};var a={text:translate.tr("set_start"),callback:setToStart,index:1};o===-1&&r.options.contextmenuItems.push(a),r.on("dragend",function(e){mapLayer.clearLayers();var o=e.target.getLatLng();autocomplete.hide(),ghRequest.route.getIndex(t).setCoord(o.lat,o.lng),resolveIndex(t),ghRequest.do_zoom=!1,routeLatLng(ghRequest,!1)})}}function resolveFrom(){return resolveIndex(0)}function resolveTo(){return resolveIndex(ghRequest.route.size()-1)}function resolveIndex(e){return setFlag(ghRequest.route.getIndex(e),e),0===e?ghRequest.to.isResolved()?mapLayer.setDisabledForMapsContextMenu("start",!1):mapLayer.setDisabledForMapsContextMenu("start",!0):e===ghRequest.route.size()-1&&(ghRequest.from.isResolved()?mapLayer.setDisabledForMapsContextMenu("end",!1):mapLayer.setDisabledForMapsContextMenu("end",!0)),nominatim.resolve(e,ghRequest.route.getIndex(e))}function resolveAll(){for(var e=[],t=0,o=ghRequest.route.size();t<o;t++)e[t]=resolveIndex(t);return e}function flagAll(){for(var e=0,t=ghRequest.route.size();e<t;e++)setFlag(ghRequest.route.getIndex(e),e)}function routeLatLng(e,t){var o=e.do_zoom;e.do_zoom=!0;var r=e.createHistoryURL()+"&layer="+tileLayers.activeLayerName;if(!t&&History.enabled){var a=urlTools.parseUrl(r);return console.log(a),a.do_zoom=o,a.mathRandom=Math.random(),void History.pushState(a,messages.browserTitle,r)}var n=$("#info");n.empty(),n.show();var s=$("<div class='route_results'/>");n.append(s),mapLayer.clearElevation(),mapLayer.clearLayers(),flagAll(),mapLayer.setDisabledForMapsContextMenu("intermediate",!1),$("#vehicles button").removeClass("selectvehicle"),$("button#"+e.getVehicle().toLowerCase()).addClass("selectvehicle");var i=e.createURL();s.html('<img src="img/indicator.gif"/> Search Route ...'),e.doRequest(i,function(t){function a(e,t,o,r,a,n){return function(){var i=e[t];mapLayer.eachLayer(function(e){if(e.setStyle){var t=e.feature===i;e.setStyle(t?d:c),t&&(L.Browser.ie||L.Browser.opera||e.bringToFront())}}),a&&(mapLayer.clearElevation(),mapLayer.addElevation(i,n)),l.find("li").removeClass("current"),s.find("div").removeClass("current"),o.addClass("current"),r.addClass("current")}}if(s.html(""),t.message){var n=t.message;if(console.log(n),t.hints)for(var i=0;i<t.hints.length;i++)s.append("<div class='error'>"+t.hints[i].message+"</div>");else s.append("<div class='error'>"+n+"</div>")}else{var l=$("<ul id='route_result_tabs'/>");t.paths.length>1&&(s.append(l),s.append("<div class='clear'/>"));for(var u,p={color:"#00cc33",weight:5,opacity:.6},d={color:"#00cc33",weight:6,opacity:.8},c={color:"darkgray",weight:6,opacity:.8},g=[],h=function(e){return function(){mapLayer.updateScale(e),ghRequest.useMiles=e,resolveAll(),routeLatLng(ghRequest)}},v=0;v<t.paths.length;v++){var m=$("<li>").append(v+1+"<img class='alt_route_img' src='img/alt_route.png'/>");0===v&&(u=m),l.append(m);var f=t.paths[v],q=0===v?p:c,y={type:"Feature",geometry:f.points,properties:{style:q,name:"route",snapped_waypoints:f.snapped_waypoints}};g.push(y),mapLayer.addDataToRoutingLayer(y);var b=$("<div class='route_result_tab'>");s.append(b),m.click(a(g,v,m,b,e.hasElevation(),e.useMiles));var R=translate.createTimeString(f.time),x=translate.createDistanceString(f.distance,e.useMiles),I=$("<div class='route_description'>");f.description&&f.description.length>0&&(I.text(f.description),I.append("<br/>")),I.append(translate.tr("route_info",[x,R]));var _=$("<button class='plain_text_button "+(e.useMiles?"gray":"")+"'>");_.text(translate.tr2("km_abbr")),_.click(h(!1));var w=$("<button class='plain_text_button "+(e.useMiles?"":"gray")+"'>");w.text(translate.tr2("mi_abbr")),w.click(h(!0));var C=$("<span style='float: right;'>");if(C.append(_),C.append("|"),C.append(w),I.append(C),e.hasElevation()&&I.append(translate.createEleInfoString(f.ascend,f.descend,e.useMiles)),b.append(I),f.instructions){var M=require("./instructions.js");b.append(M.create(mapLayer,f,r,e))}}u.click(),mapLayer.adjustMapSize();var j=t.paths[0];if(j.bbox&&o){var F=j.bbox[0],T=j.bbox[1],k=j.bbox[2],S=j.bbox[3],A=new L.LatLngBounds(new L.LatLng(T,F),new L.LatLng(S,k));mapLayer.fitMapToBounds(A)}$(".defaulting").each(function(e,t){$(t).css("color","black")})}})}function mySubmit(){var e,t,o,r=[],a=!0,n=$("#locationpoints > div.pointDiv > input.pointInput"),s=n.size;if($.each(n,function(n){0===n?(e=$(this).val(),e!==translate.tr("from_hint")&&""!==e?r.push(e):a=!1):n===s-1?(t=$(this).val(),t!==translate.tr("to_hint")&&""!==t?r.push(t):a=!1):(o=$(this).val(),o!==translate.tr("via_hint")&&""!==o?r.push(o):a=!1)}),a&&e!==translate.tr("from_hint"))return t===translate.tr("to_hint")?(ghRequest.from.setStr(e),void $.when(resolveFrom()).done(function(){mapLayer.focus(ghRequest.from,null,0)})):void(a&&resolveCoords(r))}function isProduction(){return host.indexOf("graphhopper.com")>0}global.d3=require("d3");var L=require("leaflet");require("leaflet-loading"),require("./lib/leaflet.contextmenu.js"),require("./lib/leaflet.elevation-0.0.4.min.js"),require("./lib/leaflet_numbered_markers.js"),global.jQuery=require("jquery"),global.$=global.jQuery,require("./lib/jquery-ui-custom-1.12.0.min.js"),require("./lib/jquery.history.js"),require("./lib/jquery.autocomplete.js");var ghenv=require("./config/options.js").options;console.log(ghenv.environment);var GHInput=require("./graphhopper/GHInput.js"),GHRequest=require("./graphhopper/GHRequest.js"),host=ghenv.routing.host;host||(host=""===location.port?location.protocol+"//"+location.hostname:location.protocol+"//"+location.hostname+":"+location.port);var AutoComplete=require("./autocomplete.js");if("development"===ghenv.environment)var autocomplete=AutoComplete.prototype.createStub();else var autocomplete=new AutoComplete(ghenv.geocoding.host,ghenv.geocoding.api_key);var mapLayer=require("./map.js"),nominatim=require("./nominatim.js"),routeManipulation=require("./routeManipulation.js"),gpxExport=require("./gpxexport.js"),messages=require("./messages.js"),translate=require("./translate.js"),format=require("./tools/format.js"),urlTools=require("./tools/url.js"),vehicle=require("./tools/vehicle.js"),tileLayers=require("./config/tileLayers.js"),debug=!1,ghRequest=new GHRequest(host,ghenv.routing.api_key),bounds={},metaVersionInfo;global.window&&(window.log=function(){log.history=log.history||[],log.history.push(arguments),this.console&&debug&&console.log(Array.prototype.slice.call(arguments))}),$(document).ready(function(e){jQuery.support.cors=!0,gpxExport.addGpxExport(ghRequest),isProduction()&&$("#hosting").show();var t=window.History;t.enabled&&t.Adapter.bind(window,"statechange",function(){var e=t.getState();console.log(e),initFromParams(e.data,!0)}),$("#locationform").submit(function(e){e.preventDefault(),mySubmit()});var o=urlTools.parseUrlWithHisto();$.when(ghRequest.fetchTranslationMap(o.locale),ghRequest.getInfo()).then(function(e,t){function r(e,t){var o=$("<button class='vehicle-btn' title='"+translate.tr(e)+"'/>");return t&&o.hide(),o.attr("id",e),o.html("<img src='img/"+e+".png' alt='"+translate.tr(e)+"'></img>"),o.click(function(){ghRequest.initVehicle(e),resolveAll(),routeLatLng(ghRequest)}),o}var a=e[0];autocomplete.setLocale(a.locale),ghRequest.setLocale(a.locale),translate.init(a);var n=t[0],s=n.bbox;bounds.initialized=!0,bounds.minLon=s[0],bounds.minLat=s[1],bounds.maxLon=s[2],bounds.maxLat=s[3],nominatim.setBounds(bounds);var i=$("#vehicles");if(n.features){ghRequest.features=n.features;var l={car:1,foot:2,bike:3,motorcycle:1e4},u=o.vehicle&&(!l[o.vehicle]||l[o.vehicle]>3),p=vehicle.getSortedVehicleKeys(n.features,l);p.length>0&&ghRequest.initVehicle(p[0]);var d=[];for(var c in p){var g=r(p[c].toLowerCase(),!u&&c>2);i.append(g),c>2&&d.push(g)}if(!u&&p.length>3){var h=$("<a id='more-vehicle-btn'> ...</a>").click(function(){h.hide();for(var e in d)d[e].show()});i.append($("<a class='vehicle-info-link' href='https://graphhopper.com/api/1/docs/supported-vehicle-profiles/'>?</a>")),i.append(h)}}metaVersionInfo=messages.extractMetaVersionInfo(n),mapLayer.initMap(bounds,setStartCoord,setIntermediateCoord,setEndCoord,o.layer,o.use_miles),initFromParams(o,!0),checkInput()},function(e){console.log(e),$("#error").html('GraphHopper API offline? <a href="http://graphhopper.com/maps">Refresh</a><br/>Status: '+e.statusText+"<br/>"+host),bounds={minLon:-180,minLat:-90,maxLon:180,maxLat:90},nominatim.setBounds(bounds),mapLayer.initMap(bounds,setStartCoord,setIntermediateCoord,setEndCoord,o.layer,o.use_miles)}),$(window).resize(function(){mapLayer.adjustMapSize()}),$("#locationpoints").sortable({items:".pointDiv",cursor:"n-resize",containment:"parent",handle:".pointFlag",update:function(e,t){var o=$(t.item[0]).data("index");sortable_items=$("#locationpoints > div.pointDiv"),$(sortable_items).each(function(e){var t=$(this).data("index");if(o===t)return ghRequest.route.move(t,e),routeIfAllResolved()||checkInput(),!1})}}),$("#locationpoints > div.pointAdd").click(function(){ghRequest.route.add(new GHInput),checkInput()}),checkInput()});var FROM="from",TO="to";module.exports.setFlag=setFlag;
 }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
 },{"./autocomplete.js":5,"./config/options.js":6,"./config/tileLayers.js":7,"./gpxexport.js":8,"./graphhopper/GHInput.js":9,"./graphhopper/GHRequest.js":10,"./instructions.js":13,"./lib/jquery-ui-custom-1.12.0.min.js":14,"./lib/jquery.autocomplete.js":15,"./lib/jquery.history.js":16,"./lib/leaflet.contextmenu.js":17,"./lib/leaflet.elevation-0.0.4.min.js":18,"./lib/leaflet_numbered_markers.js":19,"./map.js":21,"./messages.js":22,"./nominatim.js":23,"./routeManipulation.js":24,"./tools/format.js":25,"./tools/url.js":27,"./tools/vehicle.js":28,"./translate.js":29,"d3":1,"jquery":2,"leaflet":4,"leaflet-loading":3}],21:[function(require,module,exports){
-function adjustMapSize(){var e=$("#map"),t=$(window).width()-280;t<400?(t=400,e.attr("style","position: relative; float: right;")):e.attr("style","position: absolute; right: 0;");var a=$(window).height();a<500&&(a=500),e.width(t).height(a),$("#input").height(a);var n=a-60-$("#input_header").height()-$("#footer").height()-$(".route_description").height(),o=$("#route_result_tabs li").height();isNaN(o)||(n-=o),$(".instructions_info").css("max-height",n)}function initMap(e,t,a,n,o,r){adjustMapSize();var i=tileLayers.selectLayer(o);map=L.map("map",{layers:[i],minZoom:2,zoomSnap:0,contextmenu:!0,contextmenuWidth:150,contextmenuItems:[{separator:!0,index:3,state:["set_default"]},{text:translate.tr("show_coords"),callback:function(e){alert(e.latlng.lat+","+e.latlng.lng)},index:4,state:[1,2,3]},{text:translate.tr("center_map"),callback:function(e){map.panTo(e.latlng)},index:5,state:[1,2,3]}],zoomControl:!1,loadingControl:!1});var l={text:translate.tr("set_start"),callback:t,disabled:!1,index:0},s={text:translate.tr("set_intermediate"),callback:a,disabled:!0,index:1},m={text:translate.tr("set_end"),callback:n,disabled:!1,index:2};menuStart=map.contextmenu.insertItem(l,l.index),menuIntermediate=map.contextmenu.insertItem(s,s.index),menuEnd=map.contextmenu.insertItem(m,m.index);var d=new L.Control.Zoom({position:"topleft",zoomInTitle:translate.tr("zoom_in"),zoomOutTitle:translate.tr("zoom_out")}).addTo(map);new L.Control.loading({zoomControl:d}).addTo(map),map.contextmenu.addSet({name:"markers",state:2}),map.contextmenu.addSet({name:"path",state:3}),L.control.layers(tileLayers.getAvailableTileLayers()).addTo(map),map.on("baselayerchange",function(e){e.name&&(tileLayers.activeLayerName=e.name,$("#export-link a").attr("href",function(e,t){return t.replace(/(layer=)([\w\s]+)/,"$1"+tileLayers.activeLayerName)}))}),scaleControl=L.control.scale(r?{metric:!1}:{imperial:!1}).addTo(map),map.fitBounds(new L.LatLngBounds(new L.LatLng(e.minLat,e.minLon),new L.LatLng(e.maxLat,e.maxLon))),map.attributionControl.setPrefix("");var u={color:"black",weight:2,opacity:.3},c={type:"Feature",geometry:{type:"LineString",coordinates:[[e.minLon,e.minLat],[e.maxLon,e.minLat],[e.maxLon,e.maxLat],[e.minLon,e.maxLat],[e.minLon,e.minLat]]}};e.initialized&&L.geoJson(c,{style:u}).addTo(map),routingLayer=L.geoJson().addTo(map),routingLayer.options={style:function(e){return e.properties&&e.properties.style},contextmenu:!0,contextmenuItems:[{text:translate.tr("route")+" ",disabled:!0,index:0,state:3},{text:translate.tr("set_intermediate"),callback:a,index:1,state:3},{separator:!0,index:2,state:3}],contextmenuAtiveState:3}}function focus(e,t,a){e.lat&&e.lng&&(t||(t=11),routingLayer.clearLayers(),map.setView(new L.LatLng(e.lat,e.lng),t),mainTemplate.setFlag(e,a))}function getToFrom(e,t){return 0===e?FROM:e===t.route.size()-1?TO:-1}var mainTemplate=require("./main-template.js"),tileLayers=require("./config/tileLayers.js"),translate=require("./translate.js"),routingLayer,map,menuStart,menuIntermediate,menuEnd,elevationControl=null;module.exports.clearLayers=function(){routingLayer.clearLayers()},module.exports.getRoutingLayer=function(){return routingLayer},module.exports.getSubLayers=function(e){var t=routingLayer.getLayers();return t.filter(function(t){return t.feature&&t.feature.properties&&t.feature.properties.name===e})},module.exports.addDataToRoutingLayer=function(e){routingLayer.addData(e)},module.exports.eachLayer=function(e){routingLayer.eachLayer(e)},module.exports.setDisabledForMapsContextMenu=function(e,t){"start"===e&&map.contextmenu.setDisabled(menuStart,t),"end"===e&&map.contextmenu.setDisabled(menuEnd,t),"intermediate"===e&&map.contextmenu.setDisabled(menuIntermediate,t)},module.exports.fitMapToBounds=function(e){map.fitBounds(e,{padding:[42,42]})},module.exports.removeLayerFromMap=function(e){map.removeLayer(e)},module.exports.focus=focus,module.exports.initMap=initMap,module.exports.adjustMapSize=adjustMapSize,module.exports.addElevation=function(e,t){null===elevationControl&&(elevationControl=L.control.elevation({position:"bottomright",theme:"white-theme",width:450,height:125,margins:{top:10,right:20,bottom:30,left:60},useHeightIndicator:!0,interpolation:"linear",hoverNumber:{decimalsX:2,decimalsY:0,formatter:void 0},xTicks:void 0,yTicks:void 0,collapsed:!1}),elevationControl.addTo(map)),elevationControl.options.imperial=t,elevationControl.addData(e)},module.exports.clearElevation=function(){elevationControl&&elevationControl.clear()},module.exports.getMap=function(){return map},module.exports.updateScale=function(e){if(null!==scaleControl){scaleControl.remove();var t=e?{metric:!1}:{imperial:!1};scaleControl=L.control.scale(t).addTo(map)}};var FROM="from",TO="to",iconFrom=L.icon({iconUrl:"./img/marker-icon-green.png",shadowSize:[50,64],shadowAnchor:[4,62],iconAnchor:[12,40]}),iconTo=L.icon({iconUrl:"./img/marker-icon-red.png",shadowSize:[50,64],shadowAnchor:[4,62],iconAnchor:[12,40]});module.exports.createMarker=function(e,t,a,n,o,r){var i=getToFrom(e,r);return L.marker([t.lat,t.lng],{icon:i===FROM?iconFrom:i===TO?iconTo:new L.NumberedDivIcon({number:e}),draggable:!0,contextmenu:!0,contextmenuItems:[{text:translate.tr("marker")+" "+(i===FROM?translate.tr("start_label"):i===TO?translate.tr("end_label"):translate.tr("intermediate_label")+" "+e),disabled:!0,index:0,state:2},{text:translate.tr(i!==TO?"set_end":"set_start"),callback:i!==TO?a:n,index:2,state:2},{text:translate.tr("delete_from_route"),callback:o,index:3,state:2,disabled:i!==-1&&2===r.route.size()},{separator:!0,index:4,state:2}],contextmenuAtiveState:2}).addTo(routingLayer).bindPopup(i===FROM?translate.tr("start_label"):i===TO?translate.tr("end_label"):translate.tr("intermediate_label")+" "+e)};
+function adjustMapSize(){var e=$("#map"),t=$(window).width()-280;t<400?(t=400,e.attr("style","position: relative; float: right;")):e.attr("style","position: absolute; right: 0;");var a=$(window).height();a<500&&(a=500),e.width(t).height(a),$("#input").height(a);var n=a-60-$("#input_header").height()-$("#footer").height()-$(".route_description").height(),o=$("#route_result_tabs li").height();isNaN(o)||(n-=o),$(".instructions_info").css("max-height",n)}function initMap(e,t,a,n,o,r){adjustMapSize();var i=tileLayers.selectLayer(o);defaultContextmenuItems=[{separator:!0,index:10},{text:translate.tr("show_coords"),callback:function(e){alert(e.latlng.lat+","+e.latlng.lng)},index:11},{text:translate.tr("center_map"),callback:function(e){map.panTo(e.latlng)},index:12}],map=L.map("map",{layers:[i],minZoom:2,contextmenu:!0,contextmenuWidth:150,contextmenuItems:defaultContextmenuItems,zoomControl:!1,loadingControl:!1});var l={text:translate.tr("set_start"),callback:t,index:0},m={text:translate.tr("set_intermediate"),callback:a,disabled:!0,index:1},s={text:translate.tr("set_end"),callback:n,index:2};menuStart=map.contextmenu.insertItem(l,l.index),menuIntermediate=map.contextmenu.insertItem(m,m.index),menuEnd=map.contextmenu.insertItem(s,s.index);var u=new L.Control.Zoom({position:"topleft",zoomInTitle:translate.tr("zoom_in"),zoomOutTitle:translate.tr("zoom_out")}).addTo(map);new L.Control.loading({zoomControl:u}).addTo(map),L.control.layers(tileLayers.getAvailableTileLayers()).addTo(map),map.on("baselayerchange",function(e){e.name&&(tileLayers.activeLayerName=e.name,$("#export-link a").attr("href",function(e,t){return t.replace(/(layer=)([\w\s]+)/,"$1"+tileLayers.activeLayerName)}))}),scaleControl=L.control.scale(r?{metric:!1}:{imperial:!1}).addTo(map),map.fitBounds(new L.LatLngBounds(new L.LatLng(e.minLat,e.minLon),new L.LatLng(e.maxLat,e.maxLon))),map.attributionControl.setPrefix(!1);var d={color:"black",weight:2,opacity:.3},c={type:"Feature",geometry:{type:"LineString",coordinates:[[e.minLon,e.minLat],[e.maxLon,e.minLat],[e.maxLon,e.maxLat],[e.minLon,e.maxLat],[e.minLon,e.minLat]]}};e.initialized&&L.geoJson(c,{style:d}).addTo(map),routingLayer=L.geoJson().addTo(map),routingLayer.options={style:function(e){return e.properties&&e.properties.style},contextmenu:!0,contextmenuItems:defaultContextmenuItems.concat([{text:translate.tr("route"),disabled:!0,index:0},{text:translate.tr("set_intermediate"),callback:a,index:1}]),contextmenuInheritItems:!1}}function focus(e,t,a){e.lat&&e.lng&&(t||(t=11),routingLayer.clearLayers(),map.setView(new L.LatLng(e.lat,e.lng),t),mainTemplate.setFlag(e,a))}function getToFrom(e,t){return 0===e?FROM:e===t.route.size()-1?TO:-1}var mainTemplate=require("./main-template.js"),tileLayers=require("./config/tileLayers.js"),translate=require("./translate.js"),routingLayer,map,menuStart,menuIntermediate,menuEnd,elevationControl=null,defaultContextmenuItems;module.exports.clearLayers=function(){routingLayer.clearLayers()},module.exports.getRoutingLayer=function(){return routingLayer},module.exports.getSubLayers=function(e){var t=routingLayer.getLayers();return t.filter(function(t){return t.feature&&t.feature.properties&&t.feature.properties.name===e})},module.exports.addDataToRoutingLayer=function(e){routingLayer.addData(e)},module.exports.eachLayer=function(e){routingLayer.eachLayer(e)},module.exports.setDisabledForMapsContextMenu=function(e,t){"start"===e&&map.contextmenu.setDisabled(menuStart,t),"end"===e&&map.contextmenu.setDisabled(menuEnd,t),"intermediate"===e&&map.contextmenu.setDisabled(menuIntermediate,t)},module.exports.fitMapToBounds=function(e){map.fitBounds(e,{padding:[42,42]})},module.exports.removeLayerFromMap=function(e){map.removeLayer(e)},module.exports.focus=focus,module.exports.initMap=initMap,module.exports.adjustMapSize=adjustMapSize,module.exports.addElevation=function(e,t){null===elevationControl&&(elevationControl=L.control.elevation({position:"bottomright",theme:"white-theme",width:450,height:125,margins:{top:10,right:20,bottom:30,left:60},useHeightIndicator:!0,interpolation:"linear",hoverNumber:{decimalsX:2,decimalsY:0,formatter:void 0},xTicks:void 0,yTicks:void 0,collapsed:!1}),elevationControl.addTo(map)),elevationControl.options.imperial=t,elevationControl.addData(e)},module.exports.clearElevation=function(){elevationControl&&elevationControl.clear()},module.exports.getMap=function(){return map},module.exports.updateScale=function(e){if(null!==scaleControl){scaleControl.remove();var t=e?{metric:!1}:{imperial:!1};scaleControl=L.control.scale(t).addTo(map)}};var FROM="from",TO="to",iconFrom=L.icon({iconUrl:"./img/marker-icon-green.png",shadowSize:[50,64],shadowAnchor:[4,62],iconAnchor:[12,40]}),iconTo=L.icon({iconUrl:"./img/marker-icon-red.png",shadowSize:[50,64],shadowAnchor:[4,62],iconAnchor:[12,40]});module.exports.createMarker=function(e,t,a,n,o,r){var i=getToFrom(e,r);return L.marker([t.lat,t.lng],{icon:i===FROM?iconFrom:i===TO?iconTo:new L.NumberedDivIcon({number:e}),draggable:!0,contextmenu:!0,contextmenuItems:defaultContextmenuItems.concat([{text:translate.tr("marker")+" "+(i===FROM?translate.tr("start_label"):i===TO?translate.tr("end_label"):translate.tr("intermediate_label")+" "+e),disabled:!0,index:0},{text:translate.tr(i!==TO?"set_end":"set_start"),callback:i!==TO?a:n,index:2},{text:translate.tr("delete_from_route"),callback:o,disabled:i!==-1&&2===r.route.size(),index:3}]),contextmenuInheritItems:!1}).addTo(routingLayer).bindPopup(i===FROM?translate.tr("start_label"):i===TO?translate.tr("end_label"):translate.tr("intermediate_label")+" "+e)};
 
 },{"./config/tileLayers.js":7,"./main-template.js":20,"./translate.js":29}],22:[function(require,module,exports){
 module.exports.extractMetaVersionInfo=function(r){return metaVersionInfo="",r.data_date&&(metaVersionInfo+="<br/>Data date: "+r.data_date),r.import_date&&(metaVersionInfo+="<br/>Import date: "+r.import_date),r.prepare_date&&(metaVersionInfo+="<br/>Prepare date: "+r.prepare_date),r.version&&(metaVersionInfo+="<br/>GH version: "+r.version),r.build_date&&(metaVersionInfo+="<br/>Jar date: "+r.build_date),metaVersionInfo},module.exports.getSignName=function(r){if(r===-3)return"sharp_left";if(r===-2)return"left";if(r===-1)return"slight_left";if(0===r)return"continue";if(1===r)return"slight_right";if(2===r)return"right";if(3===r)return"sharp_right";if(4===r)return"marker-icon-red";if(5===r)return"marker-icon-blue";if(6===r)return"roundabout";throw"did not find sign "+r},module.exports.browserTitle="GraphHopper Maps - Driving Directions";
diff --git a/web/src/main/webapp/js/map.js b/web/src/main/webapp/js/map.js
index 6a20b5ab6..675db7dfa 100644
--- a/web/src/main/webapp/js/map.js
+++ b/web/src/main/webapp/js/map.js
@@ -9,6 +9,9 @@ var menuIntermediate;
 var menuEnd;
 var elevationControl = null;
 
+// Items added in every contextmenu.
+var defaultContextmenuItems;
+
 // called if window changes or before map is created
 function adjustMapSize() {
     var mapDiv = $("#map");
@@ -29,9 +32,9 @@ function adjustMapSize() {
     // console.log("adjustMapSize " + height + "x" + width);
 
     // reduce info size depending on how heigh the input_header is and reserve space for footer
-    var instructionInfoMaxHeight = height - 60
-            - $("#input_header").height() - $("#footer").height() - $(".route_description").height();
-    var tabHeight = $("#route_result_tabs li").height()
+    var instructionInfoMaxHeight = height - 60 -
+            $("#input_header").height() - $("#footer").height() - $(".route_description").height();
+    var tabHeight = $("#route_result_tabs li").height();
     if (!isNaN(tabHeight))
         instructionInfoMaxHeight -= tabHeight;
     $(".instructions_info").css("max-height", instructionInfoMaxHeight);
@@ -46,41 +49,38 @@ function initMap(bounds, setStartCoord, setIntermediateCoord, setEndCoord, selec
 
     var defaultLayer = tileLayers.selectLayer(selectLayer);
 
+    defaultContextmenuItems = [{
+        separator: true,
+        index: 10
+    }, {
+        text: translate.tr('show_coords'),
+        callback: function (e) {
+            alert(e.latlng.lat + "," + e.latlng.lng);
+        },
+        index: 11
+    }, {
+        text: translate.tr('center_map'),
+        callback: function (e) {
+            map.panTo(e.latlng);
+        },
+        index: 12
+    }];
+
     // default
     map = L.map('map', {
         layers: [defaultLayer],
         minZoom: 2,
-        zoomSnap: 0,  // allow fractional zoom levels
+        // zoomSnap: 0,  // allow fractional zoom levels
         contextmenu: true,
         contextmenuWidth: 150,
-        contextmenuItems: [{
-                separator: true,
-                index: 3,
-                state: ['set_default']
-            }, {
-                text: translate.tr('show_coords'),
-                callback: function (e) {
-                    alert(e.latlng.lat + "," + e.latlng.lng);
-                },
-                index: 4,
-                state: [1, 2, 3]
-            }, {
-                text: translate.tr('center_map'),
-                callback: function (e) {
-                    map.panTo(e.latlng);
-                },
-                index: 5,
-                state: [1, 2, 3]
-            }],
+        contextmenuItems: defaultContextmenuItems,
         zoomControl: false,
         loadingControl: false
     });
 
-
     var _startItem = {
         text: translate.tr('set_start'),
         callback: setStartCoord,
-        disabled: false,
         index: 0
     };
     var _intItem = {
@@ -92,7 +92,6 @@ function initMap(bounds, setStartCoord, setIntermediateCoord, setEndCoord, selec
     var _endItem = {
         text: translate.tr('set_end'),
         callback: setEndCoord,
-        disabled: false,
         index: 2
     };
     menuStart = map.contextmenu.insertItem(_startItem, _startItem.index);
@@ -109,16 +108,6 @@ function initMap(bounds, setStartCoord, setIntermediateCoord, setEndCoord, selec
         zoomControl: zoomControl
     }).addTo(map);
 
-    map.contextmenu.addSet({
-        name: 'markers',
-        state: 2
-    });
-
-    map.contextmenu.addSet({
-        name: 'path',
-        state: 3
-    });
-
     L.control.layers(tileLayers.getAvailableTileLayers()/*, overlays*/).addTo(map);
 
     map.on('baselayerchange', function (a) {
@@ -142,29 +131,30 @@ function initMap(bounds, setStartCoord, setIntermediateCoord, setEndCoord, selec
     //if (isProduction())
     //    map.setView(new L.LatLng(0, 0), 2);
 
-    map.attributionControl.setPrefix('');
+    map.attributionControl.setPrefix(false);
 
     var myStyle = {
-        "color": 'black',
-        "weight": 2,
-        "opacity": 0.3
+        color: 'black',
+        weight: 2,
+        opacity: 0.3
     };
     var geoJson = {
-        "type": "Feature",
-        "geometry": {
-            "type": "LineString",
-            "coordinates": [
+        type: "Feature",
+        geometry: {
+            type: "LineString",
+            coordinates: [
                 [bounds.minLon, bounds.minLat],
                 [bounds.maxLon, bounds.minLat],
                 [bounds.maxLon, bounds.maxLat],
                 [bounds.minLon, bounds.maxLat],
-                [bounds.minLon, bounds.minLat]]
+                [bounds.minLon, bounds.minLat]
+            ]
         }
     };
 
     if (bounds.initialized)
         L.geoJson(geoJson, {
-            "style": myStyle
+            style: myStyle
         }).addTo(map);
 
     routingLayer = L.geoJson().addTo(map);
@@ -175,22 +165,16 @@ function initMap(bounds, setStartCoord, setIntermediateCoord, setEndCoord, selec
             return feature.properties && feature.properties.style;
         },
         contextmenu: true,
-        contextmenuItems: [{
-                text: translate.tr('route') + ' ',
+        contextmenuItems: defaultContextmenuItems.concat([{
+                text: translate.tr('route'),
                 disabled: true,
-                index: 0,
-                state: 3
+                index: 0
             }, {
                 text: translate.tr('set_intermediate'),
                 callback: setIntermediateCoord,
-                index: 1,
-                state: 3
-            }, {
-                separator: true,
-                index: 2,
-                state: 3
-            }],
-        contextmenuAtiveState: 3
+                index: 1
+            }]),
+        contextmenuInheritItems: false
     };
 }
 
@@ -327,30 +311,23 @@ module.exports.createMarker = function (index, coord, setToEnd, setToStart, dele
         icon: ((toFrom === FROM) ? iconFrom : ((toFrom === TO) ? iconTo : new L.NumberedDivIcon({number: index}))),
         draggable: true,
         contextmenu: true,
-        contextmenuItems: [{
+        contextmenuItems: defaultContextmenuItems.concat([{
                 text: translate.tr("marker") + ' ' + ((toFrom === FROM) ?
                         translate.tr("start_label") : ((toFrom === TO) ?
                         translate.tr("end_label") : translate.tr("intermediate_label") + ' ' + index)),
                 disabled: true,
-                index: 0,
-                state: 2
+                index: 0
             }, {
                 text: translate.tr((toFrom !== TO) ? "set_end" : "set_start"),
                 callback: (toFrom !== TO) ? setToEnd : setToStart,
-                index: 2,
-                state: 2
+                index: 2
             }, {
                 text: translate.tr("delete_from_route"),
                 callback: deleteCoord,
-                index: 3,
-                state: 2,
-                disabled: (toFrom !== -1 && ghRequest.route.size() === 2) ? true : false // prevent to and from
-            }, {
-                separator: true,
-                index: 4,
-                state: 2
-            }],
-        contextmenuAtiveState: 2
+                disabled: (toFrom !== -1 && ghRequest.route.size() === 2) ? true : false, // prevent to and from
+                index: 3
+            }]),
+        contextmenuInheritItems: false
     }).addTo(routingLayer).bindPopup(((toFrom === FROM) ?
             translate.tr("start_label") : ((toFrom === TO) ?
             translate.tr("end_label") : translate.tr("intermediate_label") + ' ' + index)));


Test patch:
diff --git a/core/src/test/java/com/graphhopper/routing/util/Car4WDFlagEncoderTest.java b/core/src/test/java/com/graphhopper/routing/util/Car4WDFlagEncoderTest.java
new file mode 100644
index 000000000..2f75746e9
--- /dev/null
+++ b/core/src/test/java/com/graphhopper/routing/util/Car4WDFlagEncoderTest.java
@@ -0,0 +1,129 @@
+/*
+ *  Licensed to GraphHopper GmbH under one or more contributor
+ *  license agreements. See the NOTICE file distributed with this work for 
+ *  additional information regarding copyright ownership.
+ * 
+ *  GraphHopper GmbH licenses this file to you under the Apache License, 
+ *  Version 2.0 (the "License"); you may not use this file except in 
+ *  compliance with the License. You may obtain a copy of the License at
+ * 
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package com.graphhopper.routing.util;
+
+import com.graphhopper.reader.ReaderNode;
+import com.graphhopper.reader.ReaderWay;
+import com.graphhopper.util.Helper;
+import org.junit.Test;
+
+import java.text.DateFormat;
+import java.util.Date;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author Peter Karich
+ * @author zstadler
+ */
+public class Car4WDFlagEncoderTest extends CarFlagEncoderTest {
+    private final EncodingManager em = new EncodingManager("car4wd,bike,foot");
+    private final Car4WDFlagEncoder encoder = (Car4WDFlagEncoder) em.getEncoder("car4wd");
+
+    @Override
+    @Test
+    public void testAccess() {
+        ReaderWay way = new ReaderWay(1);
+        assertFalse(encoder.acceptWay(way) > 0);
+        way.setTag("highway", "service");
+        assertTrue(encoder.acceptWay(way) > 0);
+        way.setTag("access", "no");
+        assertFalse(encoder.acceptWay(way) > 0);
+
+        way.clearTags();
+        way.setTag("highway", "track");
+        assertTrue(encoder.acceptWay(way) > 0);
+
+        way.setTag("motorcar", "no");
+        assertFalse(encoder.acceptWay(way) > 0);
+
+        // for now allow grade1+2+3 for every country, see #253
+        way.clearTags();
+        way.setTag("highway", "track");
+        way.setTag("tracktype", "grade2");
+        assertTrue(encoder.acceptWay(way) > 0);
+	// This is the only difference from a "car"
+        way.setTag("tracktype", "grade4");
+        assertTrue(encoder.acceptWay(way) > 0);
+
+        way.clearTags();
+        way.setTag("highway", "service");
+        way.setTag("access", "delivery");
+        assertFalse(encoder.acceptWay(way) > 0);
+
+        way.clearTags();
+        way.setTag("highway", "unclassified");
+        way.setTag("ford", "yes");
+        assertFalse(encoder.acceptWay(way) > 0);
+        way.setTag("motorcar", "yes");
+        assertTrue(encoder.acceptWay(way) > 0);
+
+        way.clearTags();
+        way.setTag("route", "ferry");
+        assertTrue(encoder.acceptWay(way) > 0);
+        assertTrue(encoder.isFerry(encoder.acceptWay(way)));
+        way.setTag("motorcar", "no");
+        assertFalse(encoder.acceptWay(way) > 0);
+
+        way.clearTags();
+        way.setTag("route", "ferry");
+        way.setTag("foot", "yes");
+        assertFalse(encoder.acceptWay(way) > 0);
+        assertFalse(encoder.isFerry(encoder.acceptWay(way)));
+
+        way.clearTags();
+        way.setTag("access", "yes");
+        way.setTag("motor_vehicle", "no");
+        assertFalse(encoder.acceptWay(way) > 0);
+
+        way.clearTags();
+        way.setTag("highway", "service");
+        way.setTag("access", "yes");
+        way.setTag("motor_vehicle", "no");
+        assertFalse(encoder.acceptWay(way) > 0);
+
+        way.clearTags();
+        way.setTag("highway", "service");
+        way.setTag("access", "no");
+        way.setTag("motorcar", "yes");
+        assertTrue(encoder.acceptWay(way) > 0);
+
+        way.clearTags();
+        way.setTag("highway", "service");
+        way.setTag("access", "emergency");
+        assertFalse(encoder.acceptWay(way) > 0);
+
+        way.clearTags();
+        way.setTag("highway", "service");
+        way.setTag("motor_vehicle", "emergency");
+        assertFalse(encoder.acceptWay(way) > 0);
+
+        DateFormat simpleDateFormat = Helper.createFormatter("yyyy MMM dd");
+
+        way.clearTags();
+        way.setTag("highway", "road");
+        way.setTag("access:conditional", "no @ (" + simpleDateFormat.format(new Date().getTime()) + ")");
+        assertFalse(encoder.acceptWay(way) > 0);
+
+        way.clearTags();
+        way.setTag("highway", "road");
+        way.setTag("access", "no");
+        way.setTag("access:conditional", "yes @ (" + simpleDateFormat.format(new Date().getTime()) + ")");
+        assertTrue(encoder.acceptWay(way) > 0);
+    }
+}
diff --git a/web/src/test/webapp/spec/routeManipulationSpec.js b/web/src/test/webapp/spec/routeManipulationSpec.js
index 3d83b7f4e..b089f54dc 100644
--- a/web/src/test/webapp/spec/routeManipulationSpec.js
+++ b/web/src/test/webapp/spec/routeManipulationSpec.js
@@ -1,5 +1,8 @@
-// because leaflet-src.js requires window, document etc. we need to provide these variables...
-global.window = {};
+// Because leaflet-src.js requires window, document, and navigator we need to
+// provide these variables.
+global.window = {
+    screen: {}
+};
 global.document = {
     documentElement: {
         style: {}
@@ -8,6 +11,7 @@ global.document = {
     createElement: function() { return {}; }
 };
 global.navigator = {
+    platform: 'nodejs',
     userAgent: 'nodejs'
 };
 
@@ -46,7 +50,7 @@ describe('getIntermediatePointIndex', function () {
             coordinates: [a, x1, x2, x3, b, x4, x5, c, x6, x7, d],
             wayPoints: [a, b, c, d]
         }];
-        
+
         for(var i=0; i < clickLocations.length; ++i) {
             expect(routeManipulation.getIntermediatePointIndex(routeSegments, clickLocations[i].latlng))
                     .toEqual(clickLocations[i].expectedIndex);
@@ -97,7 +101,7 @@ describe('getIntermediatePointIndex', function () {
             coordinates: [c, x6, x7, d],
             wayPoints: wayPoints
         }];
-    
+
         for(var i=0; i < clickLocations.length; ++i) {
             expect(routeManipulation.getIntermediatePointIndex(routeSegments, clickLocations[i].latlng))
                     .toEqual(clickLocations[i].expectedIndex);