Index: applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/utils/MapillaryURL.java
===================================================================
--- applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/utils/MapillaryURL.java	(revision 31829)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/utils/MapillaryURL.java	(revision 31831)
@@ -1,2 +1,3 @@
+// License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.plugins.mapillary.utils;
 
@@ -23,4 +24,9 @@
   }
 
+  /**
+   * Gives you the URL for the online editor of a specific mapillary image.
+   * @param key the key of the image to which you want to link
+   * @return the URL of the online editor for the image with the given image key
+   */
   public static URL browseEditURL(String key) {
     if (key == null || !key.matches("[a-zA-Z0-9\\-_]{22}")) {
@@ -30,4 +36,9 @@
   }
 
+  /**
+   * Gives you the URL for the online viewer of a specific mapillary image.
+   * @param key the key of the image to which you want to link
+   * @return the URL of the online viewer for the image with the given image key
+   */
   public static URL browseImageURL(String key) {
     if (key == null || !key.matches("[a-zA-Z0-9\\-_]{22}")) {
@@ -37,11 +48,21 @@
   }
 
+  /**
+   * @return the URL where the user can view all uploaded images that are not yet published
+   */
   public static URL browseUploadImageURL() {
-    return string2URL(BASE_WEBSITE_URL + "map/upload/im");
+    return string2URL(BASE_WEBSITE_URL + "map/upload/im/");
   }
 
+  /**
+   * Gives you the URL which the user should visit to initiate the OAuth authentication process
+   * @param redirectURI the URI to which the user will be redirected when the authentication is finished
+   * @return the URL that the user should visit to start the OAuth authentication
+   */
   public static URL connectURL(String redirectURI) {
     HashMap<String, String> parts = new HashMap<>();
-    parts.put("redirect_uri", redirectURI);
+    if (redirectURI != null && redirectURI.length() >= 1) {
+      parts.put("redirect_uri", redirectURI);
+    }
     parts.put("response_type", "token");
     parts.put("scope", "user:read public:upload public:write");
@@ -49,4 +70,11 @@
   }
 
+  /**
+   * Gives you the API-URL where you get 20 images within the given bounds.
+   * For more than 20 images you have to use different URLs with different page numbers.
+   * @param bounds the bounds in which you want to search for images
+   * @param page number of the page to retrieve from the API
+   * @return the API-URL which gives you the images in the given bounds as JSON
+   */
   public static URL searchImageURL(Bounds bounds, int page) {
     HashMap<String, String> parts = new HashMap<>();
@@ -57,4 +85,11 @@
   }
 
+  /**
+   * Gives you the API-URL where you get 10 sequences within the given bounds.
+   * For more than 10 sequences you have to use different URLs with different page numbers.
+   * @param bounds the bounds in which you want to search for sequences
+   * @param page number of the page to retrieve from the API
+   * @return the API-URL which gives you the sequences in the given bounds as JSON
+   */
   public static URL searchSequenceURL(Bounds bounds, int page) {
     HashMap<String, String> parts = new HashMap<>();
@@ -65,4 +100,11 @@
   }
 
+  /**
+   * Gives you the API-URL where you get the traffic signs for 20 images within the given bounds.
+   * For the signs from more than 20 images you have to use different URLs with different page numbers.
+   * @param bounds the bounds in which you want to search for traffic signs
+   * @param page number of the page to retrieve from the API
+   * @return the API-URL which gives you the traffic signs in the given bounds as JSON
+   */
   public static URL searchTrafficSignURL(Bounds bounds, int page) {
     HashMap<String, String> parts = new HashMap<>();
@@ -73,21 +115,39 @@
   }
 
+  /**
+   * @return the URL where you'll find the upload secrets as JSON
+   */
   public static URL uploadSecretsURL() {
     return string2URL(BASE_API_URL + "me/uploads/secrets/" + queryString(null));
   }
 
+  /**
+   * @return the URL where you'll find information about the user account as JSON
+   */
   public static URL userURL() {
     return string2URL(BASE_API_URL + "me/" + queryString(null));
   }
 
+  /**
+   * Adds the given {@link Bounds} to a {@link Map} that contains the parts of a query string.
+   * @param parts the parts of a query string
+   * @param bounds the bounds that will be added to the query string
+   */
   private static void putBoundsInQueryStringParts(Map<String, String> parts, Bounds bounds) {
-    parts.put("min_lat", String.format(Locale.UK, "%f", bounds.getMin().lat()));
-    parts.put("max_lat", String.format(Locale.UK, "%f", bounds.getMax().lat()));
-    parts.put("min_lon", String.format(Locale.UK, "%f", bounds.getMin().lon()));
-    parts.put("max_lon", String.format(Locale.UK, "%f", bounds.getMax().lon()));
+    if (bounds != null) {
+      parts.put("min_lat", String.format(Locale.UK, "%f", bounds.getMin().lat()));
+      parts.put("max_lat", String.format(Locale.UK, "%f", bounds.getMax().lat()));
+      parts.put("min_lon", String.format(Locale.UK, "%f", bounds.getMin().lon()));
+      parts.put("max_lon", String.format(Locale.UK, "%f", bounds.getMax().lon()));
+    }
   }
 
+  /**
+   * Builds a query string from it's parts that are supplied as a {@link Map}
+   * @param parts the parts of the query string
+   * @return the constructed query string (including a leading ?)
+   */
   private static String queryString(Map<String, String> parts) {
-    StringBuilder ret = new StringBuilder().append("?client_id=").append(CLIENT_ID);
+    StringBuilder ret = new StringBuilder("?client_id=").append(CLIENT_ID);
     if (parts != null) {
       for (Entry<String, String> entry : parts.entrySet()) {
@@ -105,9 +165,16 @@
   }
 
+  /**
+   * Converts a {@link String} into a {@link URL} without throwing a {@link MalformedURLException}.
+   * Instead such an exception will lead to an {@link Main#error(Throwable)}.
+   * So you should be very confident that your URL is well-formed when calling this method.
+   * @param string the String describing the URL
+   * @return the URL that is constructed from the given string
+   */
   private static URL string2URL(String string) {
     try {
       return new URL(string);
     } catch (MalformedURLException e) {
-      Main.error("The MapillaryAPI class produces malformed URLs!", e);
+      Main.error(new Exception("The "+MapillaryURL.class.getSimpleName()+" class produces malformed URLs!", e));
       return null;
     }
