Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/properties/HelpAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/properties/HelpAction.java	(revision 14640)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/properties/HelpAction.java	(revision 14641)
@@ -7,6 +7,4 @@
 import java.awt.event.KeyEvent;
 import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -15,5 +13,4 @@
 import java.util.Objects;
 import java.util.function.IntFunction;
-import java.util.stream.Collectors;
 
 import javax.swing.AbstractAction;
@@ -21,21 +18,15 @@
 import javax.swing.KeyStroke;
 import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.xpath.XPath;
-import javax.xml.xpath.XPathConstants;
 import javax.xml.xpath.XPathExpressionException;
-import javax.xml.xpath.XPathFactory;
 
 import org.openstreetmap.josm.data.osm.IRelation;
 import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.spi.preferences.Config;
-import org.openstreetmap.josm.tools.HttpClient;
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.LanguageInfo;
 import org.openstreetmap.josm.tools.Logging;
+import org.openstreetmap.josm.tools.Mediawiki;
 import org.openstreetmap.josm.tools.OpenBrowser;
 import org.openstreetmap.josm.tools.Utils;
-import org.openstreetmap.josm.tools.XmlUtils;
-import org.w3c.dom.Document;
-import org.w3c.dom.Node;
 import org.xml.sax.SAXException;
 
@@ -164,30 +155,7 @@
     public static void displayHelp(final List<String> pages) {
         try {
-            // find a page that actually exists in the wiki
-            // API documentation: https://wiki.openstreetmap.org/w/api.php?action=help&modules=query
-            final URL url = new URL(Config.getUrls().getOSMWiki() + "/w/api.php?action=query&format=xml&titles=" + pages.stream()
-                    .map(Utils::encodeUrl)
-                    .collect(Collectors.joining("|"))
-            );
-            final HttpClient.Response conn = HttpClient.create(url).connect();
-            final Document document;
-            try (InputStream content = conn.getContent()) {
-                document = XmlUtils.parseSafeDOM(content);
-            }
-            conn.disconnect();
-            final XPath xPath = XPathFactory.newInstance().newXPath();
-            for (String page : pages) {
-                String normalized = xPath.evaluate("/api/query/normalized/n[@from='" + page + "']/@to", document);
-                if (normalized == null || normalized.isEmpty()) {
-                    normalized = page;
-                }
-                final Node node = (Node) xPath.evaluate("/api/query/pages/page[@title='" + normalized + "']", document, XPathConstants.NODE);
-                if (node != null
-                        && node.getAttributes().getNamedItem("missing") == null
-                        && node.getAttributes().getNamedItem("invalid") == null) {
-                    OpenBrowser.displayUrl(Config.getUrls().getOSMWiki() + "/wiki/" + page);
-                    break;
-                }
-            }
+            new Mediawiki(Config.getUrls().getOSMWiki())
+                    .findExistingPage(pages)
+                    .ifPresent(page -> OpenBrowser.displayUrl(Config.getUrls().getOSMWiki() + "/wiki/" + page));
         } catch (IOException | ParserConfigurationException | XPathExpressionException | SAXException e1) {
             Logging.error(e1);
Index: /trunk/src/org/openstreetmap/josm/tools/Mediawiki.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/tools/Mediawiki.java	(revision 14641)
+++ /trunk/src/org/openstreetmap/josm/tools/Mediawiki.java	(revision 14641)
@@ -0,0 +1,75 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.tools;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
+/**
+ * Interaction with Mediawiki instances, such as the OSM wiki.
+ * @since 14641
+ */
+public class Mediawiki {
+
+    private final String baseUrl;
+
+    /**
+     * Constructs a new {@code Mediawiki} for the given base URL.
+     * @param baseUrl The wiki base URL
+     */
+    public Mediawiki(String baseUrl) {
+        this.baseUrl = baseUrl;
+    }
+
+    /**
+     * Determines which page exists on the Mediawiki instance.
+     * @param pages the pages to check
+     * @return the first existing page
+     * @throws IOException if any I/O error occurs
+     * @throws ParserConfigurationException if a parser cannot be created
+     * @throws SAXException if any XML error occurs
+     * @throws XPathExpressionException if any error in an XPath expression occurs
+     */
+    public Optional<String> findExistingPage(List<String> pages)
+            throws IOException, ParserConfigurationException, SAXException, XPathExpressionException {
+        // find a page that actually exists in the wiki
+        // API documentation: https://wiki.openstreetmap.org/w/api.php?action=help&modules=query
+        final URL url = new URL(baseUrl + "/w/api.php?action=query&format=xml&titles=" + pages.stream()
+                .map(Utils::encodeUrl)
+                .collect(Collectors.joining("|"))
+        );
+        final HttpClient.Response conn = HttpClient.create(url).connect();
+        final Document document;
+        try (InputStream content = conn.getContent()) {
+            document = XmlUtils.parseSafeDOM(content);
+        }
+        conn.disconnect();
+        final XPath xPath = XPathFactory.newInstance().newXPath();
+        for (String page : pages) {
+            String normalized = xPath.evaluate("/api/query/normalized/n[@from='" + page + "']/@to", document);
+            if (normalized == null || normalized.isEmpty()) {
+                normalized = page;
+            }
+            final Node node = (Node) xPath.evaluate("/api/query/pages/page[@title='" + normalized + "']", document, XPathConstants.NODE);
+            if (node != null
+                    && node.getAttributes().getNamedItem("missing") == null
+                    && node.getAttributes().getNamedItem("invalid") == null) {
+                return Optional.of(page);
+            }
+        }
+        return Optional.empty();
+    }
+}
