Index: /trunk/src/org/openstreetmap/josm/Main.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/Main.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/Main.java	(revision 7434)
@@ -25,8 +25,10 @@
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 import java.util.StringTokenizer;
 import java.util.concurrent.Callable;
@@ -91,4 +93,5 @@
 import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
 import org.openstreetmap.josm.io.FileWatcher;
+import org.openstreetmap.josm.io.OnlineResource;
 import org.openstreetmap.josm.io.OsmApi;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
@@ -219,4 +222,6 @@
     private static final List<String> ERRORS_AND_WARNINGS = Collections.<String>synchronizedList(new ArrayList<String>());
 
+    private static final Set<OnlineResource> OFFLINE_RESOURCES = new HashSet<>();
+
     /**
      * Logging level (5 = trace, 4 = debug, 3 = info, 2 = warn, 1 = error, 0 = none).
@@ -543,18 +548,22 @@
         List<Callable<Void>> tasks = new ArrayList<>();
 
-        tasks.add(new InitializationTask(tr("Initializing OSM API")) {
-
-            @Override
-            public void initialize() throws Exception {
-                // We try to establish an API connection early, so that any API
-                // capabilities are already known to the editor instance. However
-                // if it goes wrong that's not critical at this stage.
-                try {
-                    OsmApi.getOsmApi().initialize(null, true);
-                } catch (Exception e) {
-                    Main.warn(getErrorMessage(Utils.getRootCause(e)));
+        if (isOffline(OnlineResource.OSM_API)) {
+            Main.warn(tr("{0} not available (offline mode)", tr("OSM API")));
+        } else {
+            tasks.add(new InitializationTask(tr("Initializing OSM API")) {
+
+                @Override
+                public void initialize() throws Exception {
+                    // We try to establish an API connection early, so that any API
+                    // capabilities are already known to the editor instance. However
+                    // if it goes wrong that's not critical at this stage.
+                    try {
+                        OsmApi.getOsmApi().initialize(null, true);
+                    } catch (Exception e) {
+                        Main.warn(getErrorMessage(Utils.getRootCause(e)));
+                    }
                 }
-            }
-        });
+            });
+        }
 
         tasks.add(new InitializationTask(tr("Initializing validator")) {
@@ -1553,3 +1562,32 @@
         return Main.platform instanceof PlatformHookWindows;
     }
+
+    /**
+     * Determines if the given online resource is currently offline.
+     * @param r the online resource
+     * @return {@code true} if {@code r} is offline and should not be accessed
+     * @since 7434
+     */
+    public static boolean isOffline(OnlineResource r) {
+        return OFFLINE_RESOURCES.contains(r) || OFFLINE_RESOURCES.contains(OnlineResource.ALL);
+    }
+
+    /**
+     * Sets the given online resource to offline state.
+     * @param r the online resource
+     * @return {@code true} if {@code r} was not already offline
+     * @since 7434
+     */
+    public static boolean setOffline(OnlineResource r) {
+        return OFFLINE_RESOURCES.add(r);
+    }
+
+    /**
+     * Replies the set of online resources currently offline.
+     * @return the set of online resources currently offline
+     * @since 7434
+     */
+    public static Set<OnlineResource> getOfflineResources() {
+        return new HashSet<>(OFFLINE_RESOURCES);
+    }
 }
Index: /trunk/src/org/openstreetmap/josm/actions/CloseChangesetAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/CloseChangesetAction.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/actions/CloseChangesetAction.java	(revision 7434)
@@ -23,4 +23,5 @@
 import org.openstreetmap.josm.gui.io.CloseChangesetTask;
 import org.openstreetmap.josm.io.ChangesetQuery;
+import org.openstreetmap.josm.io.OnlineResource;
 import org.openstreetmap.josm.io.OsmServerChangesetReader;
 import org.openstreetmap.josm.io.OsmServerUserInfoReader;
@@ -29,6 +30,9 @@
 import org.xml.sax.SAXException;
 
-public class CloseChangesetAction extends JosmAction{
+public class CloseChangesetAction extends JosmAction {
 
+    /**
+     * Constructs a new {@code CloseChangesetAction}.
+     */
     public CloseChangesetAction() {
         super(tr("Close open changesets"),
@@ -41,4 +45,5 @@
         );
         putValue("help", ht("/Action/CloseChangeset"));
+        setEnabled(!Main.isOffline(OnlineResource.OSM_API));
 
     }
Index: /trunk/src/org/openstreetmap/josm/actions/DeleteAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/DeleteAction.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/actions/DeleteAction.java	(revision 7434)
@@ -13,6 +13,13 @@
 import org.openstreetmap.josm.tools.Shortcut;
 
+/**
+ * Action that deletes selected objects.
+ * @since 770
+ */
 public final class DeleteAction extends JosmAction {
 
+    /**
+     * Constructs a new {@code DeleteAction}.
+     */
     public DeleteAction() {
         super(tr("Delete"), "dialogs/delete", tr("Delete selected objects."),
@@ -23,7 +30,5 @@
     @Override
     public void actionPerformed(ActionEvent e) {
-        if (!isEnabled())
-            return;
-        if(!Main.map.mapView.isActiveLayerVisible())
+        if (!isEnabled() || !Main.map.mapView.isActiveLayerVisible())
             return;
         org.openstreetmap.josm.actions.mapmode.DeleteAction.doActionPerformed(e);
Index: /trunk/src/org/openstreetmap/josm/actions/HelpAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/HelpAction.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/actions/HelpAction.java	(revision 7434)
@@ -14,15 +14,20 @@
 import org.openstreetmap.josm.gui.help.HelpBrowser;
 import org.openstreetmap.josm.gui.help.HelpUtil;
+import org.openstreetmap.josm.io.OnlineResource;
 import org.openstreetmap.josm.tools.ImageProvider;
 
 /**
  * Open a help browser and displays lightweight online help.
- *
+ * @since 155
  */
 public class HelpAction extends AbstractAction {
 
+    /**
+     * Constructs a new {@code HelpAction}.
+     */
     public HelpAction() {
         super(tr("Help"), ImageProvider.get("help"));
         putValue("toolbar", "help");
+        setEnabled(!Main.isOffline(OnlineResource.JOSM_WEBSITE));
     }
 
Index: /trunk/src/org/openstreetmap/josm/actions/HistoryInfoAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/HistoryInfoAction.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/actions/HistoryInfoAction.java	(revision 7434)
@@ -1,4 +1,7 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.actions;
+
+import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
+import static org.openstreetmap.josm.tools.I18n.tr;
 
 import java.awt.event.ActionEvent;
@@ -9,8 +12,6 @@
 import org.openstreetmap.josm.gui.dialogs.OsmIdSelectionDialog;
 import org.openstreetmap.josm.gui.history.HistoryBrowserDialogManager;
+import org.openstreetmap.josm.io.OnlineResource;
 import org.openstreetmap.josm.tools.Shortcut;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
 
 /**
@@ -61,4 +62,10 @@
             init();
         }
+
+        @Override
+        public void setupDialog() {
+            super.setupDialog();
+            buttons.get(0).setEnabled(!Main.isOffline(OnlineResource.OSM_API));
+        }
     }
 }
Index: /trunk/src/org/openstreetmap/josm/actions/OpenLocationAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/OpenLocationAction.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/actions/OpenLocationAction.java	(revision 7434)
@@ -73,6 +73,5 @@
     protected void restoreUploadAddressHistory(HistoryComboBox cbHistory) {
         List<String> cmtHistory = new LinkedList<>(Main.pref.getCollection(getClass().getName() + ".uploadAddressHistory", new LinkedList<String>()));
-        // we have to reverse the history, because ComboBoxHistory will reverse it again
-        // in addElement()
+        // we have to reverse the history, because ComboBoxHistory will reverse it again in addElement()
         //
         Collections.reverse(cmtHistory);
Index: /trunk/src/org/openstreetmap/josm/actions/UpdateDataAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/UpdateDataAction.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/actions/UpdateDataAction.java	(revision 7434)
@@ -15,9 +15,13 @@
 import org.openstreetmap.josm.actions.downloadtasks.DownloadTaskList;
 import org.openstreetmap.josm.data.osm.DataSource;
-import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor;
+import org.openstreetmap.josm.io.OnlineResource;
 import org.openstreetmap.josm.tools.Shortcut;
 
-public class UpdateDataAction extends JosmAction{
+public class UpdateDataAction extends JosmAction {
+
+    /**
+     * Constructs a new {@code UpdateDataAction}.
+     */
     public UpdateDataAction() {
         super(tr("Update data"),
@@ -33,13 +37,8 @@
     /**
      * Refreshes the enabled state
-     *
      */
     @Override
     protected void updateEnabledState() {
-        setEnabled(getEditLayer() != null);
-    }
-
-    public void updateLayer(OsmDataLayer layer) {
-
+        setEnabled(getEditLayer() != null && !Main.isOffline(OnlineResource.OSM_API));
     }
 
Index: /trunk/src/org/openstreetmap/josm/actions/UpdateModifiedAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/UpdateModifiedAction.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/actions/UpdateModifiedAction.java	(revision 7434)
@@ -9,5 +9,7 @@
 import java.util.Collections;
 
+import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.io.OnlineResource;
 import org.openstreetmap.josm.tools.Shortcut;
 
@@ -44,5 +46,5 @@
     @Override
     protected void updateEnabledState() {
-        setEnabled(getCurrentDataSet() != null);
+        setEnabled(getCurrentDataSet() != null && !Main.isOffline(OnlineResource.OSM_API));
     }
 
Index: /trunk/src/org/openstreetmap/josm/actions/UpdateSelectionAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/UpdateSelectionAction.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/actions/UpdateSelectionAction.java	(revision 7434)
@@ -22,4 +22,5 @@
 import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
 import org.openstreetmap.josm.io.MultiFetchServerObjectReader;
+import org.openstreetmap.josm.io.OnlineResource;
 import org.openstreetmap.josm.tools.Shortcut;
 
@@ -122,5 +123,5 @@
     @Override
     protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) {
-        setEnabled(selection != null && !selection.isEmpty());
+        setEnabled(selection != null && !selection.isEmpty() && !Main.isOffline(OnlineResource.OSM_API));
     }
 
Index: /trunk/src/org/openstreetmap/josm/actions/mapmode/DeleteAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/mapmode/DeleteAction.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/actions/mapmode/DeleteAction.java	(revision 7434)
@@ -129,6 +129,10 @@
     }
 
+    /**
+     * Invoked when the action occurs.
+     * @param e Action event
+     */
     public static void doActionPerformed(ActionEvent e) {
-        if(!Main.map.mapView.isActiveLayerDrawable())
+        if (!Main.map.mapView.isActiveLayerDrawable())
             return;
         boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
@@ -149,5 +153,6 @@
     }
 
-    @Override public void mouseDragged(MouseEvent e) {
+    @Override
+    public void mouseDragged(MouseEvent e) {
         mouseMoved(e);
     }
@@ -157,5 +162,6 @@
      * @param e The mouse event that has been captured
      */
-    @Override public void mouseMoved(MouseEvent e) {
+    @Override
+    public void mouseMoved(MouseEvent e) {
         oldEvent = e;
         giveUserFeedback(e);
@@ -245,4 +251,5 @@
         Main.map.mapView.setNewCursor(parameters.mode.cursor(), this);
     }
+
     /**
      * Gives the user feedback for the action he/she is about to do. Currently
@@ -274,5 +281,6 @@
      * position.
      */
-    @Override public void mouseReleased(MouseEvent e) {
+    @Override
+    public void mouseReleased(MouseEvent e) {
         if (e.getButton() != MouseEvent.BUTTON1)
             return;
@@ -293,9 +301,11 @@
     }
 
-    @Override public String getModeHelpText() {
+    @Override
+    public String getModeHelpText() {
         return tr("Click to delete. Shift: delete way segment. Alt: do not delete unused nodes when deleting a way. Ctrl: delete referring objects.");
     }
 
-    @Override public boolean layerIsSupported(Layer l) {
+    @Override
+    public boolean layerIsSupported(Layer l) {
         return l instanceof OsmDataLayer;
     }
@@ -391,8 +401,7 @@
     @Override
     public void modifiersChanged(int modifiers) {
-        if(oldEvent == null)
-            return;
-        // We don't have a mouse event, so we pass the old mouse event but the
-        // new modifiers.
+        if (oldEvent == null)
+            return;
+        // We don't have a mouse event, so we pass the old mouse event but the new modifiers.
         giveUserFeedback(oldEvent, modifiers);
     }
Index: /trunk/src/org/openstreetmap/josm/actions/relation/DownloadMembersAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/relation/DownloadMembersAction.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/actions/relation/DownloadMembersAction.java	(revision 7434)
@@ -12,4 +12,5 @@
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.gui.dialogs.relation.DownloadRelationTask;
+import org.openstreetmap.josm.io.OnlineResource;
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.Predicate;
@@ -31,5 +32,5 @@
         putValue("help", ht("/Dialog/RelationList#DownloadMembers"));
     }
-    
+
     @Override
     public void actionPerformed(ActionEvent e) {
@@ -47,3 +48,8 @@
         updateEnabledState();
     }
+
+    @Override
+    protected void updateEnabledState() {
+        setEnabled(!relations.isEmpty() && !Main.isOffline(OnlineResource.OSM_API));
+    }
 }
Index: /trunk/src/org/openstreetmap/josm/actions/relation/DownloadSelectedIncompleteMembersAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/relation/DownloadSelectedIncompleteMembersAction.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/actions/relation/DownloadSelectedIncompleteMembersAction.java	(revision 7434)
@@ -13,4 +13,5 @@
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.gui.dialogs.relation.DownloadRelationMemberTask;
+import org.openstreetmap.josm.io.OnlineResource;
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.Predicate;
@@ -63,3 +64,8 @@
         updateEnabledState();
     }
+
+    @Override
+    protected void updateEnabledState() {
+        setEnabled(!relations.isEmpty() && !Main.isOffline(OnlineResource.OSM_API));
+    }
 }
Index: /trunk/src/org/openstreetmap/josm/actions/upload/ApiPreconditionCheckerHook.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/upload/ApiPreconditionCheckerHook.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/actions/upload/ApiPreconditionCheckerHook.java	(revision 7434)
@@ -15,4 +15,5 @@
 import org.openstreetmap.josm.gui.ExceptionDialogUtil;
 import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
+import org.openstreetmap.josm.io.OnlineResource;
 import org.openstreetmap.josm.io.OsmApi;
 import org.openstreetmap.josm.io.OsmApiInitializationException;
@@ -25,4 +26,7 @@
         OsmApi api = OsmApi.getOsmApi();
         try {
+            if (Main.isOffline(OnlineResource.OSM_API)) {
+                return false;
+            }
             // FIXME: this should run asynchronously and a progress monitor
             // should be displayed.
@@ -40,5 +44,5 @@
                     return false;
             }
-        } catch(OsmTransferCanceledException e){
+        } catch (OsmTransferCanceledException e) {
             return false;
         } catch (OsmApiInitializationException e) {
Index: /trunk/src/org/openstreetmap/josm/data/imagery/ImageryLayerInfo.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/imagery/ImageryLayerInfo.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/data/imagery/ImageryLayerInfo.java	(revision 7434)
@@ -14,7 +14,10 @@
 import java.util.Set;
 import java.util.TreeSet;
+
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryPreferenceEntry;
 import org.openstreetmap.josm.io.CachedFile;
+import org.openstreetmap.josm.io.OfflineAccessException;
+import org.openstreetmap.josm.io.OnlineResource;
 import org.openstreetmap.josm.io.imagery.ImageryReader;
 import org.xml.sax.SAXException;
@@ -34,4 +37,13 @@
         Main.getJOSMWebsite()+"/maps"
     };
+
+    /**
+     * Returns the list of imagery layers sites.
+     * @return the list of imagery layers sites
+     * @since 7434
+     */
+    public static Collection<String> getImageryLayersSites() {
+        return Main.pref.getCollection("imagery.layers.sites", Arrays.asList(DEFAULT_LAYER_SITES));
+    }
 
     private ImageryLayerInfo() {
@@ -76,6 +88,13 @@
         defaultLayers.clear();
         defaultLayerIds.clear();
-        for (String source : Main.pref.getCollection("imagery.layers.sites", Arrays.asList(DEFAULT_LAYER_SITES))) {
-            if (clearCache) {
+        for (String source : getImageryLayersSites()) {
+            boolean online = true;
+            try {
+                OnlineResource.JOSM_WEBSITE.checkOfflineAccess(source, Main.getJOSMWebsite());
+            } catch (OfflineAccessException e) {
+                Main.warn(e.getMessage());
+                online = false;
+            }
+            if (clearCache && online) {
                 CachedFile.cleanup(source);
             }
@@ -96,5 +115,5 @@
         buildIdMap(layers, layerIds);
     }
-    
+
     /**
      * Build the mapping of unique ids to {@link ImageryInfo}s.
@@ -120,5 +139,5 @@
         }
     }
-    
+
     /**
      * Update user entries according to the list of default entries.
@@ -179,5 +198,5 @@
         }
         Main.pref.putCollection("imagery.layers.addedIds", newAddedIds);
-        
+
         // automatically update user entries with same id as a default entry
         for (int i=0; i<layers.size(); i++) {
@@ -192,5 +211,5 @@
             }
         }
-        
+
         if (changed) {
             save();
@@ -202,10 +221,10 @@
         return isSimilar(iiA.getUrl(), iiB.getUrl());
     }
-    
+
     // some additional checks to respect extended URLs in preferences (legacy workaround)
     private boolean isSimilar(String a, String b) {
         return Objects.equals(a, b) || (a != null && b != null && !a.isEmpty() && !b.isEmpty() && (a.contains(b) || b.contains(a)));
     }
-    
+
     public void add(ImageryInfo info) {
         layers.add(info);
@@ -244,8 +263,8 @@
         Collections.sort(instance.layers);
     }
-    
+
     /**
      * Get unique id for ImageryInfo.
-     * 
+     *
      * This takes care, that no id is used twice (due to a user error)
      * @param info the ImageryInfo to look up
Index: /trunk/src/org/openstreetmap/josm/gui/ExceptionDialogUtil.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/ExceptionDialogUtil.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/gui/ExceptionDialogUtil.java	(revision 7434)
@@ -20,4 +20,5 @@
 import org.openstreetmap.josm.io.IllegalDataException;
 import org.openstreetmap.josm.io.MissingOAuthAccessTokenException;
+import org.openstreetmap.josm.io.OfflineAccessException;
 import org.openstreetmap.josm.io.OsmApi;
 import org.openstreetmap.josm.io.OsmApiException;
@@ -154,5 +155,4 @@
      * @param e the exception
      */
-
     public static void explainNestedIllegalDataException(OsmTransferException e) {
         HelpAwareOptionPane.showOptionDialog(
@@ -166,9 +166,25 @@
 
     /**
+     * Explains a {@link OfflineAccessException} which has caused an {@link OsmTransferException}.
+     * This is most likely happening when JOSM tries to access OSM API or JOSM website while in offline mode.
+     *
+     * @param e the exception
+     * @since 7434
+     */
+    public static void explainNestedOfflineAccessException(OsmTransferException e) {
+        HelpAwareOptionPane.showOptionDialog(
+                Main.parent,
+                ExceptionUtil.explainOfflineAccessException(e),
+                tr("Offline mode"),
+                JOptionPane.ERROR_MESSAGE,
+                ht("/ErrorMessages#OfflineAccessException")
+        );
+    }
+
+    /**
      * Explains a {@link InvocationTargetException }
      *
      * @param e the exception
      */
-
     public static void explainNestedInvocationTargetException(Exception e) {
         InvocationTargetException ex = getNestedException(e, InvocationTargetException.class);
@@ -435,4 +451,8 @@
             return;
         }
+        if (getNestedException(e, OfflineAccessException.class) != null) {
+            explainNestedOfflineAccessException(e);
+            return;
+        }
         if (e instanceof OsmApiInitializationException) {
             explainOsmApiInitializationException((OsmApiInitializationException) e);
Index: /trunk/src/org/openstreetmap/josm/gui/ExtendedDialog.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/ExtendedDialog.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/gui/ExtendedDialog.java	(revision 7434)
@@ -30,8 +30,10 @@
 import javax.swing.UIManager;
 
+import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.gui.help.HelpBrowser;
 import org.openstreetmap.josm.gui.help.HelpUtil;
 import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
+import org.openstreetmap.josm.io.OnlineResource;
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.ImageProvider;
@@ -642,7 +644,9 @@
             putValue(NAME, tr("Help"));
             putValue(SMALL_ICON, ImageProvider.get("help"));
-        }
-
-        @Override public void actionPerformed(ActionEvent e) {
+            setEnabled(!Main.isOffline(OnlineResource.JOSM_WEBSITE));
+        }
+
+        @Override
+        public void actionPerformed(ActionEvent e) {
             HelpBrowser.setUrlForHelpTopic(helpTopic);
         }
Index: /trunk/src/org/openstreetmap/josm/gui/GettingStarted.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/GettingStarted.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/gui/GettingStarted.java	(revision 7434)
@@ -30,4 +30,5 @@
 import org.openstreetmap.josm.gui.widgets.JosmEditorPane;
 import org.openstreetmap.josm.io.CacheCustomContent;
+import org.openstreetmap.josm.io.OnlineResource;
 import org.openstreetmap.josm.tools.LanguageInfo;
 import org.openstreetmap.josm.tools.OpenBrowser;
@@ -95,4 +96,9 @@
         }
 
+        @Override
+        protected void checkOfflineAccess() {
+            OnlineResource.JOSM_WEBSITE.checkOfflineAccess(new WikiReader().getBaseUrlWiki(), Main.getJOSMWebsite());
+        }
+
         /**
          * Additionally check if JOSM has been updated and refresh MOTD
@@ -151,10 +157,12 @@
                 }
 
-                EventQueue.invokeLater(new Runnable() {
-                    @Override
-                    public void run() {
-                        lg.setText(fixImageLinks(content));
-                    }
-                });
+                if (content != null) {
+                    EventQueue.invokeLater(new Runnable() {
+                        @Override
+                        public void run() {
+                            lg.setText(fixImageLinks(content));
+                        }
+                    });
+                }
             }
         }, "MOTD-Loader");
Index: /trunk/src/org/openstreetmap/josm/gui/JosmUserIdentityManager.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/JosmUserIdentityManager.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/gui/JosmUserIdentityManager.java	(revision 7434)
@@ -14,4 +14,5 @@
 import org.openstreetmap.josm.gui.preferences.server.OAuthAccessTokenHolder;
 import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
+import org.openstreetmap.josm.io.OnlineResource;
 import org.openstreetmap.josm.io.OsmApi;
 import org.openstreetmap.josm.io.OsmServerUserInfoReader;
@@ -61,5 +62,6 @@
         if (instance == null) {
             instance = new JosmUserIdentityManager();
-            if (OsmApi.isUsingOAuth() && OAuthAccessTokenHolder.getInstance().containsAccessToken()) {
+            if (OsmApi.isUsingOAuth() && OAuthAccessTokenHolder.getInstance().containsAccessToken() &&
+                    !Main.isOffline(OnlineResource.OSM_API)) {
                 try {
                     instance.initFromOAuth(Main.parent);
Index: /trunk/src/org/openstreetmap/josm/gui/MainApplication.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/MainApplication.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/gui/MainApplication.java	(revision 7434)
@@ -27,4 +27,5 @@
 import java.security.cert.CertificateException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
@@ -54,4 +55,5 @@
 import org.openstreetmap.josm.io.DefaultProxySelector;
 import org.openstreetmap.josm.io.MessageNotifier;
+import org.openstreetmap.josm.io.OnlineResource;
 import org.openstreetmap.josm.io.auth.CredentialsManager;
 import org.openstreetmap.josm.io.auth.DefaultAuthenticator;
@@ -87,5 +89,5 @@
         mainFrame.setJMenuBar(menu);
         geometry.applySafe(mainFrame);
-        LinkedList<Image> l = new LinkedList<>();
+        List<Image> l = new LinkedList<>();
         l.add(ImageProvider.get("logo_16x16x32").getImage());
         l.add(ImageProvider.get("logo_16x16x8").getImage());
@@ -131,4 +133,5 @@
                 "\t--version                                 "+tr("Displays the JOSM version and exits")+"\n\n"+
                 "\t--debug                                   "+tr("Print debugging messages to console")+"\n\n"+
+                "\t--offline=<osm_api|josm_website|all>      "+tr("Disable access to the given resource(s), separated by comma")+"\n\n"+
                 tr("options provided as Java system properties")+":\n"+
                 "\t-Djosm.home="+tr("/PATH/TO/JOSM/FOLDER/         ")+tr("Change the folder for all user settings")+"\n\n"+
@@ -154,35 +157,37 @@
      */
     public enum Option {
-        /** --help|-h                                 Show this help */
+        /** --help|-h                                  Show this help */
         HELP(false),
-        /** --version                                 Displays the JOSM version and exits */
+        /** --version                                  Displays the JOSM version and exits */
         VERSION(false),
-        /** --debug                                   Print debugging messages to console */
+        /** --debug                                    Print debugging messages to console */
         DEBUG(false),
-        /** --trace                                   Print detailed debugging messages to console */
+        /** --trace                                    Print detailed debugging messages to console */
         TRACE(false),
-        /** --language=&lt;language&gt;               Set the language */
+        /** --language=&lt;language&gt;                Set the language */
         LANGUAGE(true),
-        /** --reset-preferences                       Reset the preferences to default */
+        /** --reset-preferences                        Reset the preferences to default */
         RESET_PREFERENCES(false),
-        /** --load-preferences=&lt;url-to-xml&gt;     Changes preferences according to the XML file */
+        /** --load-preferences=&lt;url-to-xml&gt;      Changes preferences according to the XML file */
         LOAD_PREFERENCES(true),
-        /** --set=&lt;key&gt;=&lt;value&gt;           Set preference key to value */
+        /** --set=&lt;key&gt;=&lt;value&gt;            Set preference key to value */
         SET(true),
-        /** --geometry=widthxheight(+|-)x(+|-)y       Standard unix geometry argument */
+        /** --geometry=widthxheight(+|-)x(+|-)y        Standard unix geometry argument */
         GEOMETRY(true),
-        /** --no-maximize                             Do not launch in maximized mode */
+        /** --no-maximize                              Do not launch in maximized mode */
         NO_MAXIMIZE(false),
-        /** --maximize                                Launch in maximized mode */
+        /** --maximize                                 Launch in maximized mode */
         MAXIMIZE(false),
-        /** --download=minlat,minlon,maxlat,maxlon    Download the bounding box <br>
-         *  --download=&lt;URL&gt;                    Download the location at the URL (with lat=x&amp;lon=y&amp;zoom=z) <br>
-         *  --download=&lt;filename&gt;               Open a file (any file type that can be opened with File/Open) */
+        /** --download=minlat,minlon,maxlat,maxlon     Download the bounding box <br>
+         *  --download=&lt;URL&gt;                     Download the location at the URL (with lat=x&amp;lon=y&amp;zoom=z) <br>
+         *  --download=&lt;filename&gt;                Open a file (any file type that can be opened with File/Open) */
         DOWNLOAD(true),
-        /** --downloadgps=minlat,minlon,maxlat,maxlon Download the bounding box as raw GPS <br>
-         *  --downloadgps=&lt;URL&gt;                 Download the location at the URL (with lat=x&amp;lon=y&amp;zoom=z) as raw GPS */
+        /** --downloadgps=minlat,minlon,maxlat,maxlon  Download the bounding box as raw GPS <br>
+         *  --downloadgps=&lt;URL&gt;                  Download the location at the URL (with lat=x&amp;lon=y&amp;zoom=z) as raw GPS */
         DOWNLOADGPS(true),
-        /** --selection=&lt;searchstring&gt;          Select with the given search */
-        SELECTION(true);
+        /** --selection=&lt;searchstring&gt;           Select with the given search */
+        SELECTION(true),
+        /** --offline=&lt;osm_api|josm_website|all&gt; Disable access to the given resource(s), delimited by comma */
+        OFFLINE(true);
 
         private String name;
@@ -284,4 +289,5 @@
         } catch (IllegalArgumentException e) {
             System.exit(1);
+            return;
         }
 
@@ -347,6 +353,8 @@
         Main.pref.updateSystemProperties();
 
+        processOffline(args);
+
         FontsManager.initialize();
-        
+
         final JFrame mainFrame = new JFrame(tr("Java OpenStreetMap Editor"));
         Main.parent = mainFrame;
@@ -466,4 +474,25 @@
             info("Enabled EDT checker, wrongful access to gui from non EDT thread will be printed to console");
             RepaintManager.setCurrentManager(new CheckThreadViolationRepaintManager());
+        }
+    }
+
+    private static void processOffline(Map<Option, Collection<String>> args) {
+        if (args.containsKey(Option.OFFLINE)) {
+            for (String s : args.get(Option.OFFLINE).iterator().next().split(",")) {
+                try {
+                    Main.setOffline(OnlineResource.valueOf(s.toUpperCase()));
+                } catch (IllegalArgumentException e) {
+                    Main.error(tr("''{0}'' is not a valid value for argument ''{1}''. Possible values are {2}, possibly delimited by commas.",
+                            s.toUpperCase(), Option.OFFLINE.getName(), Arrays.toString(OnlineResource.values())));
+                    System.exit(1);
+                    return;
+                }
+            }
+            Set<OnlineResource> offline = Main.getOfflineResources();
+            if (!offline.isEmpty()) {
+                Main.warn(trn("JOSM is running in offline mode. This resource will not be available: {0}",
+                        "JOSM is running in offline mode. These resources will not be available: {0}",
+                        offline.size(), offline.size() == 1 ? offline.iterator().next() : Arrays.toString(offline.toArray())));
+            }
         }
     }
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/ChangesetDialog.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/ChangesetDialog.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/ChangesetDialog.java	(revision 7434)
@@ -52,6 +52,8 @@
 import org.openstreetmap.josm.gui.io.CloseChangesetTask;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.openstreetmap.josm.gui.widgets.ListPopupMenu;
 import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
+import org.openstreetmap.josm.io.OnlineResource;
 import org.openstreetmap.josm.tools.BugReportExceptionHandler;
 import org.openstreetmap.josm.tools.ImageProvider;
@@ -363,5 +365,5 @@
 
         protected void updateEnabledState() {
-            setEnabled(getCurrentChangesetList().getSelectedIndices().length > 0);
+            setEnabled(getCurrentChangesetList().getSelectedIndices().length > 0 && !Main.isOffline(OnlineResource.OSM_API));
         }
 
@@ -486,8 +488,10 @@
             Set<Integer> sel = model.getSelectedChangesetIds();
             final Set<Integer> toDownload = new HashSet<>();
-            ChangesetCache cc = ChangesetCache.getInstance();
-            for (int id: sel) {
-                if (!cc.contains(id)) {
-                    toDownload.add(id);
+            if (!Main.isOffline(OnlineResource.OSM_API)) {
+                ChangesetCache cc = ChangesetCache.getInstance();
+                for (int id: sel) {
+                    if (!cc.contains(id)) {
+                        toDownload.add(id);
+                    }
                 }
             }
@@ -506,6 +510,5 @@
                 @Override
                 public void run() {
-                    // first, wait for the download task to finish, if a download
-                    // task was launched
+                    // first, wait for the download task to finish, if a download task was launched
                     if (future != null) {
                         try {
@@ -521,6 +524,5 @@
                     if (task != null) {
                         if (task.isCanceled())
-                            // don't launch the changeset manager if the download task
-                            // was canceled
+                            // don't launch the changeset manager if the download task was canceled
                             return;
                         if (task.isFailed()) {
@@ -529,5 +531,10 @@
                     }
                     // launch the task
-                    launchChangesetManager(toDownload);
+                    GuiHelper.runInEDT(new Runnable() {
+                        @Override
+                        public void run() {
+                            launchChangesetManager(toDownload);
+                        }
+                    });
                 }
             };
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/HistoryDialog.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/HistoryDialog.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/HistoryDialog.java	(revision 7434)
@@ -42,4 +42,5 @@
 import org.openstreetmap.josm.gui.history.HistoryBrowserDialogManager;
 import org.openstreetmap.josm.gui.history.HistoryLoadTask;
+import org.openstreetmap.josm.io.OnlineResource;
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.InputMapUtils;
@@ -286,5 +287,5 @@
 
         protected void updateEnabledState() {
-            setEnabled(historyTable.getSelectedRowCount() > 0);
+            setEnabled(historyTable.getSelectedRowCount() > 0 && !Main.isOffline(OnlineResource.OSM_API));
         }
 
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetCacheManager.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetCacheManager.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetCacheManager.java	(revision 7434)
@@ -49,4 +49,5 @@
 import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
 import org.openstreetmap.josm.io.ChangesetQuery;
+import org.openstreetmap.josm.io.OnlineResource;
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.WindowGeometry;
@@ -364,4 +365,5 @@
             putValue(SMALL_ICON, ImageProvider.get("dialogs","search"));
             putValue(SHORT_DESCRIPTION, tr("Launch the dialog for querying changesets"));
+            setEnabled(!Main.isOffline(OnlineResource.OSM_API));
         }
 
@@ -476,5 +478,5 @@
 
         protected void updateEnabledState() {
-            setEnabled(model.hasSelectedChangesets());
+            setEnabled(model.hasSelectedChangesets() && !Main.isOffline(OnlineResource.OSM_API));
         }
 
@@ -504,5 +506,5 @@
 
         protected void updateEnabledState() {
-            setEnabled(model.hasSelectedChangesets());
+            setEnabled(model.hasSelectedChangesets() && !Main.isOffline(OnlineResource.OSM_API));
         }
 
@@ -532,4 +534,5 @@
             putValue(SMALL_ICON, ImageProvider.get("dialogs/changeset", "downloadchangeset"));
             putValue(SHORT_DESCRIPTION, tr("Download my changesets from the OSM server (max. 100 changesets)"));
+            setEnabled(!Main.isOffline(OnlineResource.OSM_API));
         }
 
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetDetailPanel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetDetailPanel.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetDetailPanel.java	(revision 7434)
@@ -40,4 +40,5 @@
 import org.openstreetmap.josm.gui.widgets.JosmTextArea;
 import org.openstreetmap.josm.gui.widgets.JosmTextField;
+import org.openstreetmap.josm.io.OnlineResource;
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.date.DateUtils;
@@ -345,10 +346,5 @@
 
         public void initProperties(Changeset cs) {
-            if (cs == null) {
-                setEnabled(false);
-                return;
-            } else {
-                setEnabled(true);
-            }
+            setEnabled(cs != null && !Main.isOffline(OnlineResource.OSM_API));
         }
     }
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/SingleChangesetDownloadPanel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/SingleChangesetDownloadPanel.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/SingleChangesetDownloadPanel.java	(revision 7434)
@@ -20,4 +20,5 @@
 import org.openstreetmap.josm.gui.widgets.ChangesetIdTextField;
 import org.openstreetmap.josm.gui.widgets.SelectAllOnFocusGainedDecorator;
+import org.openstreetmap.josm.io.OnlineResource;
 
 /**
@@ -98,5 +99,5 @@
 
         protected void updateEnabledState() {
-            setEnabled(tfChangesetId.readIds());
+            setEnabled(tfChangesetId.readIds() && !Main.isOffline(OnlineResource.OSM_API));
         }
 
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java	(revision 7434)
@@ -83,4 +83,5 @@
 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingTextField;
 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionList;
+import org.openstreetmap.josm.io.OnlineResource;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
 import org.openstreetmap.josm.tools.ImageProvider;
@@ -212,5 +213,5 @@
                 }
         );
-        registerCopyPasteAction(tagEditorPanel.getPasteAction(), 
+        registerCopyPasteAction(tagEditorPanel.getPasteAction(),
                 "PASTE_TAGS",
                 Shortcut.registerShortcut("system:pastestyle", tr("Edit: {0}", tr("Paste Tags")), KeyEvent.VK_V, Shortcut.CTRL_SHIFT).getKeyStroke());
@@ -1475,5 +1476,5 @@
 
         protected void updateEnabledState() {
-            setEnabled(memberTableModel.hasIncompleteMembers());
+            setEnabled(memberTableModel.hasIncompleteMembers() && !Main.isOffline(OnlineResource.OSM_API));
         }
 
@@ -1507,5 +1508,5 @@
 
         protected void updateEnabledState() {
-            setEnabled(memberTableModel.hasIncompleteSelectedMembers());
+            setEnabled(memberTableModel.hasIncompleteSelectedMembers() && !Main.isOffline(OnlineResource.OSM_API));
         }
 
Index: /trunk/src/org/openstreetmap/josm/gui/download/DownloadDialog.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/download/DownloadDialog.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/gui/download/DownloadDialog.java	(revision 7434)
@@ -39,4 +39,5 @@
 import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction;
 import org.openstreetmap.josm.gui.help.HelpUtil;
+import org.openstreetmap.josm.io.OnlineResource;
 import org.openstreetmap.josm.plugins.PluginHandler;
 import org.openstreetmap.josm.tools.GBC;
@@ -103,5 +104,5 @@
 
         slippyMapChooser = new SlippyMapChooser();
-        
+
         // predefined download selections
         downloadSelections.add(slippyMapChooser);
@@ -305,5 +306,5 @@
         }
     }
-    
+
     /**
      * Remembers the current settings in the download dialog.
@@ -349,5 +350,5 @@
         }
     }
-    
+
     /**
      * Returns the previously saved bounding box from preferences.
@@ -444,4 +445,5 @@
             putValue(SMALL_ICON, ImageProvider.get("download"));
             putValue(SHORT_DESCRIPTION, tr("Click to download the currently selected area"));
+            setEnabled(!Main.isOffline(OnlineResource.OSM_API));
         }
 
Index: /trunk/src/org/openstreetmap/josm/gui/download/DownloadObjectDialog.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/download/DownloadObjectDialog.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/gui/download/DownloadObjectDialog.java	(revision 7434)
@@ -16,4 +16,5 @@
 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
 import org.openstreetmap.josm.gui.dialogs.OsmIdSelectionDialog;
+import org.openstreetmap.josm.io.OnlineResource;
 
 /**
@@ -49,4 +50,11 @@
     }
 
+    @Override
+    public void setupDialog() {
+        super.setupDialog();
+        buttons.get(0).setEnabled(!Main.isOffline(OnlineResource.OSM_API));
+    }
+
+    @Override
     protected Collection<Component> getComponentsBeforeHelp() {
         newLayer.setToolTipText(tr("Select if the data should be downloaded into a new layer"));
Index: /trunk/src/org/openstreetmap/josm/gui/help/ContextSensitiveHelpAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/help/ContextSensitiveHelpAction.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/gui/help/ContextSensitiveHelpAction.java	(revision 7434)
@@ -2,10 +2,13 @@
 package org.openstreetmap.josm.gui.help;
 
+import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
 import java.awt.event.ActionEvent;
-import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
 
 import javax.swing.AbstractAction;
-import static org.openstreetmap.josm.tools.I18n.tr;
 
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.io.OnlineResource;
 import org.openstreetmap.josm.tools.ImageProvider;
 
@@ -13,13 +16,12 @@
  * This is the standard help action to be used with help buttons for
  * context sensitive help
- *
+ * @since 2289
  */
 public class ContextSensitiveHelpAction extends AbstractAction {
 
-    /** the help topic */
     private String helpTopic;
 
     /**
-     * Sets the help topic
+     * Sets the help topic.
      *
      * @param relativeHelpTopic the relative help topic
@@ -32,6 +34,5 @@
 
     /**
-     * Creates a help topic for the root help topic
-     *
+     * Constructs a new {@code ContextSensitiveHelpAction} for the root help topic.
      */
     public ContextSensitiveHelpAction() {
@@ -40,6 +41,6 @@
 
     /**
-     *
-     * @param helpTopic
+     * Constructs a new {@code ContextSensitiveHelpAction} for a given help topic.
+     * @param helpTopic The help topic
      */
     public ContextSensitiveHelpAction(String helpTopic) {
@@ -48,4 +49,5 @@
         putValue(SMALL_ICON, ImageProvider.get("help"));
         this.helpTopic = helpTopic;
+        setEnabled(!Main.isOffline(OnlineResource.JOSM_WEBSITE));
     }
 
Index: /trunk/src/org/openstreetmap/josm/gui/preferences/SourceEditor.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/SourceEditor.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/gui/preferences/SourceEditor.java	(revision 7434)
@@ -87,8 +87,10 @@
 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
 import org.openstreetmap.josm.gui.util.FileFilterAllFiles;
+import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.openstreetmap.josm.gui.util.TableHelper;
 import org.openstreetmap.josm.gui.widgets.JFileChooserManager;
 import org.openstreetmap.josm.gui.widgets.JosmTextField;
 import org.openstreetmap.josm.io.CachedFile;
+import org.openstreetmap.josm.io.OnlineResource;
 import org.openstreetmap.josm.io.OsmTransferException;
 import org.openstreetmap.josm.tools.GBC;
@@ -1041,4 +1043,5 @@
             this.url = url;
             this.sourceProviders = sourceProviders;
+            setEnabled(!Main.isOffline(OnlineResource.JOSM_WEBSITE));
         }
 
@@ -1256,13 +1259,18 @@
             String emsg = e.getMessage() != null ? e.getMessage() : e.toString();
             emsg = emsg.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
-            String msg = tr(getStr(I18nString.FAILED_TO_LOAD_SOURCES_FROM), url, emsg);
-
-            HelpAwareOptionPane.showOptionDialog(
-                    Main.parent,
-                    msg,
-                    tr("Error"),
-                    JOptionPane.ERROR_MESSAGE,
-                    ht(getStr(I18nString.FAILED_TO_LOAD_SOURCES_FROM_HELP_TOPIC))
-                    );
+            final String msg = tr(getStr(I18nString.FAILED_TO_LOAD_SOURCES_FROM), url, emsg);
+
+            GuiHelper.runInEDT(new Runnable() {
+                @Override
+                public void run() {
+                    HelpAwareOptionPane.showOptionDialog(
+                            Main.parent,
+                            msg,
+                            tr("Error"),
+                            JOptionPane.ERROR_MESSAGE,
+                            ht(getStr(I18nString.FAILED_TO_LOAD_SOURCES_FROM_HELP_TOPIC))
+                            );
+                }
+            });
         }
 
Index: /trunk/src/org/openstreetmap/josm/gui/preferences/plugin/PluginPreference.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/plugin/PluginPreference.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/gui/preferences/plugin/PluginPreference.java	(revision 7434)
@@ -50,4 +50,6 @@
 import org.openstreetmap.josm.gui.widgets.JosmTextField;
 import org.openstreetmap.josm.gui.widgets.SelectAllOnFocusGainedDecorator;
+import org.openstreetmap.josm.io.OfflineAccessException;
+import org.openstreetmap.josm.io.OnlineResource;
 import org.openstreetmap.josm.plugins.PluginDownloadTask;
 import org.openstreetmap.josm.plugins.PluginInformation;
@@ -114,5 +116,5 @@
         return sb.toString();
     }
-    
+
     /**
      * Notifies user about result of a finished plugin download task.
@@ -313,7 +315,19 @@
     }
 
+    private static Collection<String> getOnlinePluginSites() {
+        Collection<String> pluginSites = new ArrayList<>(Main.pref.getPluginSites());
+        for (Iterator<String> it = pluginSites.iterator(); it.hasNext();) {
+            try {
+                OnlineResource.JOSM_WEBSITE.checkOfflineAccess(it.next(), Main.getJOSMWebsite());
+            } catch (OfflineAccessException ex) {
+                Main.warn(ex.getMessage());
+                it.remove();
+            }
+        }
+        return pluginSites;
+    }
+
     /**
      * The action for downloading the list of available plugins
-     *
      */
     class DownloadAvailablePluginsAction extends AbstractAction {
@@ -327,5 +341,9 @@
         @Override
         public void actionPerformed(ActionEvent e) {
-            final ReadRemotePluginInformationTask task = new ReadRemotePluginInformationTask(Main.pref.getPluginSites());
+            Collection<String> pluginSites = getOnlinePluginSites();
+            if (pluginSites.isEmpty()) {
+                return;
+            }
+            final ReadRemotePluginInformationTask task = new ReadRemotePluginInformationTask(pluginSites);
             Runnable continuation = new Runnable() {
                 @Override
@@ -345,9 +363,9 @@
             Main.worker.submit(continuation);
         }
-    }
-
-    /**
-     * The action for downloading the list of available plugins
-     *
+
+    }
+
+    /**
+     * The action for updating the list of selected plugins
      */
     class UpdateSelectedPluginsAction extends AbstractAction {
@@ -387,5 +405,5 @@
                     );
             // the async task for downloading plugin information
-            final ReadRemotePluginInformationTask pluginInfoDownloadTask = new ReadRemotePluginInformationTask(Main.pref.getPluginSites());
+            final ReadRemotePluginInformationTask pluginInfoDownloadTask = new ReadRemotePluginInformationTask(getOnlinePluginSites());
 
             // to be run asynchronously after the plugin download
Index: /trunk/src/org/openstreetmap/josm/io/CacheCustomContent.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/CacheCustomContent.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/io/CacheCustomContent.java	(revision 7434)
@@ -80,4 +80,25 @@
     }
 
+    private boolean needsUpdate() {
+        if (isOffline()) {
+            return false;
+        }
+        return Main.pref.getInteger("cache." + ident, 0) + updateInterval < System.currentTimeMillis()/1000
+                || !isCacheValid();
+    }
+
+    private boolean isOffline() {
+        try {
+            checkOfflineAccess();
+            return false;
+        } catch (OfflineAccessException e) {
+            return true;
+        }
+    }
+
+    protected void checkOfflineAccess() {
+        // To be overriden by subclasses
+    }
+
     /**
      * Updates data if required
@@ -85,6 +106,5 @@
      */
     public byte[] updateIfRequired() throws T {
-        if (Main.pref.getInteger("cache." + ident, 0) + updateInterval < System.currentTimeMillis()/1000
-                || !isCacheValid())
+        if (needsUpdate())
             return updateForce();
         return getData();
@@ -96,6 +116,5 @@
      */
     public String updateIfRequiredString() throws T {
-        if (Main.pref.getInteger("cache." + ident, 0) + updateInterval < System.currentTimeMillis()/1000
-                || !isCacheValid())
+        if (needsUpdate())
             return updateForceString();
         return getDataString();
@@ -138,9 +157,13 @@
      */
     public String getDataString() throws T {
-        return new String(getData(), StandardCharsets.UTF_8);
+        byte[] array = getData();
+        if (array == null) {
+            return null;
+        }
+        return new String(array, StandardCharsets.UTF_8);
     }
 
     /**
-     * Tries to load the data using the given ident from disk. If this fails, data will be updated
+     * Tries to load the data using the given ident from disk. If this fails, data will be updated, unless run in offline mode
      */
     private void loadFromDisk() throws T {
@@ -149,5 +172,7 @@
             input.read(this.data);
         } catch (IOException e) {
-            this.data = updateForce();
+            if (!isOffline()) {
+                this.data = updateForce();
+            }
         }
     }
@@ -166,6 +191,5 @@
 
     /**
-     * Flushes the data from memory. Class automatically reloads it from disk or updateData() if
-     * required
+     * Flushes the data from memory. Class automatically reloads it from disk or updateData() if required
      */
     public void flushData() {
Index: /trunk/src/org/openstreetmap/josm/io/CachedFile.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/CachedFile.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/io/CachedFile.java	(revision 7434)
@@ -24,4 +24,5 @@
 
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.tools.CheckParameterUtil;
 import org.openstreetmap.josm.tools.Pair;
 import org.openstreetmap.josm.tools.Utils;
@@ -29,5 +30,5 @@
 /**
  * Downloads a file and caches it on disk in order to reduce network load.
- * 
+ *
  * Supports URLs, local files, and a custom scheme (<code>resource:</code>) to get
  * resources from the current *.jar file. (Local caching is only done for URLs.)
@@ -49,5 +50,5 @@
          * consider the cache stale and try to download the file again.
          */
-        MaxAge, 
+        MaxAge,
         /**
          * Similar to MaxAge, considers the cache stale when a certain age is
@@ -56,5 +57,5 @@
          * as a full download.
          */
-        IfModifiedSince 
+        IfModifiedSince
     }
     protected String name;
@@ -63,5 +64,5 @@
     protected String httpAccept;
     protected CachingStrategy cachingStrategy;
-    
+
     protected File cacheFile = null;
     boolean initialized = false;
@@ -98,5 +99,5 @@
         return this;
     }
-    
+
     /**
      * Set maximum age of cache file. Only applies to URLs.
@@ -201,5 +202,5 @@
                 cacheFile = checkLocal(url);
             }
-        } catch (java.net.MalformedURLException e) {
+        } catch (MalformedURLException e) {
             if (name.startsWith("resource://")) {
                 return null;
@@ -211,8 +212,8 @@
         }
         if (cacheFile == null)
-            throw new IOException();
+            throw new IOException("Unable to get cache file for "+name);
         return cacheFile;
     }
-    
+
     /**
      * Looks for a certain entry inside a zip file and returns the entry path.
@@ -292,5 +293,5 @@
      * Clear the cache for the given resource.
      * This forces a fresh download.
-     * @param name the URL 
+     * @param name the URL
      */
     public static void cleanup(String name) {
@@ -341,4 +342,5 @@
     private File checkLocal(URL url) throws IOException {
         String prefKey = getPrefKey(url, destDir);
+        String urlStr = url.toExternalForm();
         long age = 0L;
         long lMaxAge = maxAge;
@@ -346,9 +348,15 @@
         File localFile = null;
         List<String> localPathEntry = new ArrayList<>(Main.pref.getCollection(prefKey));
+        boolean offline = false;
+        try {
+            checkOfflineAccess(urlStr);
+        } catch (OfflineAccessException e) {
+            offline = true;
+        }
         if (localPathEntry.size() == 2) {
             localFile = new File(localPathEntry.get(1));
-            if(!localFile.exists())
+            if (!localFile.exists()) {
                 localFile = null;
-            else {
+            } else {
                 if ( maxAge == DEFAULT_MAXTIME
                         || maxAge <= 0 // arbitrary value <= 0 is deprecated
@@ -357,5 +365,5 @@
                 }
                 age = System.currentTimeMillis() - Long.parseLong(localPathEntry.get(0));
-                if (age < lMaxAge*1000) {
+                if (offline || age < lMaxAge*1000) {
                     return localFile;
                 }
@@ -373,6 +381,11 @@
             destDirFile.mkdirs();
         }
-        
-        String a = url.toString().replaceAll("[^A-Za-z0-9_.-]", "_");
+
+        // No local file + offline => nothing to do
+        if (offline) {
+            return null;
+        }
+
+        String a = urlStr.replaceAll("[^A-Za-z0-9_.-]", "_");
         String localPath = "mirror_" + a;
         destDirFile = new File(destDir, localPath + ".tmp");
@@ -380,10 +393,13 @@
             HttpURLConnection con = connectFollowingRedirect(url, httpAccept, ifModifiedSince);
             if (ifModifiedSince != null && con.getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED) {
-                Main.debug("304 Not Modified ("+url+")");
-                if (localFile == null) throw new AssertionError();
-                Main.pref.putCollection(prefKey, 
+                if (Main.isDebugEnabled()) {
+                    Main.debug("304 Not Modified ("+urlStr+")");
+                }
+                if (localFile == null)
+                    throw new AssertionError();
+                Main.pref.putCollection(prefKey,
                         Arrays.asList(Long.toString(System.currentTimeMillis()), localPathEntry.get(1)));
                 return localFile;
-            } 
+            }
             try (
                 InputStream bis = new BufferedInputStream(con.getInputStream());
@@ -398,6 +414,6 @@
             }
             localFile = new File(destDir, localPath);
-            if(Main.platform.rename(destDirFile, localFile)) {
-                Main.pref.putCollection(prefKey, 
+            if (Main.platform.rename(destDirFile, localFile)) {
+                Main.pref.putCollection(prefKey,
                         Arrays.asList(Long.toString(System.currentTimeMillis()), localFile.toString()));
             } else {
@@ -407,5 +423,5 @@
         } catch (IOException e) {
             if (age >= lMaxAge*1000 && age < lMaxAge*1000*2) {
-                Main.warn(tr("Failed to load {0}, use cached file and retry next time: {1}", url, e));
+                Main.warn(tr("Failed to load {0}, use cached file and retry next time: {1}", urlStr, e));
                 return localFile;
             } else {
@@ -415,4 +431,9 @@
 
         return localFile;
+    }
+
+    private static void checkOfflineAccess(String urlString) {
+        OnlineResource.JOSM_WEBSITE.checkOfflineAccess(urlString, Main.getJOSMWebsite());
+        OnlineResource.OSM_API.checkOfflineAccess(urlString, Main.pref.get("osm-server.url", OsmApi.DEFAULT_API_URL));
     }
 
@@ -424,5 +445,5 @@
      * is going from a http to a https URL, see <a href="https://bugs.openjdk.java.net/browse/JDK-4620571">bug report</a>.
      * <p>
-     * This can causes problems when downloading from certain GitHub URLs.
+     * This can cause problems when downloading from certain GitHub URLs.
      *
      * @param downloadUrl The resource URL to download
@@ -432,11 +453,20 @@
      * @throws MalformedURLException If a redirected URL is wrong
      * @throws IOException If any I/O operation goes wrong
+     * @throws OfflineAccessException if resource is accessed in offline mode, in any protocol
      * @since 6867
      */
     public static HttpURLConnection connectFollowingRedirect(URL downloadUrl, String httpAccept, Long ifModifiedSince) throws MalformedURLException, IOException {
+        CheckParameterUtil.ensureParameterNotNull(downloadUrl, "downloadUrl");
+        String downloadString = downloadUrl.toExternalForm();
+
+        checkOfflineAccess(downloadString);
+
         HttpURLConnection con = null;
         int numRedirects = 0;
         while(true) {
             con = Utils.openHttpConnection(downloadUrl);
+            if (con == null) {
+                throw new IOException("Cannot open http connection to "+downloadString);
+            }
             if (ifModifiedSince != null) {
                 con.setIfModifiedSince(ifModifiedSince);
@@ -445,7 +475,11 @@
             con.setConnectTimeout(Main.pref.getInteger("socket.timeout.connect",15)*1000);
             con.setReadTimeout(Main.pref.getInteger("socket.timeout.read",30)*1000);
-            Main.debug("GET "+downloadUrl);
+            if (Main.isDebugEnabled()) {
+                Main.debug("GET "+downloadString);
+            }
             if (httpAccept != null) {
-                Main.debug("Accept: "+httpAccept);
+                if (Main.isTraceEnabled()) {
+                    Main.trace("Accept: "+httpAccept);
+                }
                 con.setRequestProperty("Accept", httpAccept);
             }
@@ -466,9 +500,12 @@
             case HttpURLConnection.HTTP_SEE_OTHER:
                 String redirectLocation = con.getHeaderField("Location");
-                if (downloadUrl == null) {
-                    /* I18n: argument is HTTP response code */ String msg = tr("Unexpected response from HTTP server. Got {0} response without ''Location'' header. Can''t redirect. Aborting.", con.getResponseCode());
+                if (redirectLocation == null) {
+                    /* I18n: argument is HTTP response code */
+                    String msg = tr("Unexpected response from HTTP server. Got {0} response without ''Location'' header."+
+                            " Can''t redirect. Aborting.", con.getResponseCode());
                     throw new IOException(msg);
                 }
                 downloadUrl = new URL(redirectLocation);
+                downloadString = downloadUrl.toExternalForm();
                 // keep track of redirect attempts to break a redirect loops if it happens
                 // to occur for whatever reason
@@ -478,12 +515,11 @@
                     throw new IOException(msg);
                 }
-                Main.info(tr("Download redirected to ''{0}''", downloadUrl));
+                Main.info(tr("Download redirected to ''{0}''", downloadString));
                 break;
             default:
-                String msg = tr("Failed to read from ''{0}''. Server responded with status code {1}.", downloadUrl, con.getResponseCode());
+                String msg = tr("Failed to read from ''{0}''. Server responded with status code {1}.", downloadString, con.getResponseCode());
                 throw new IOException(msg);
             }
         }
     }
-
 }
Index: /trunk/src/org/openstreetmap/josm/io/MessageNotifier.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/MessageNotifier.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/io/MessageNotifier.java	(revision 7434)
@@ -40,16 +40,16 @@
         // Hide default constructor for utils classes
     }
-    
+
     /** Property defining if this task is enabled or not */
     public static final BooleanProperty PROP_NOTIFIER_ENABLED = new BooleanProperty("message.notifier.enabled", true);
     /** Property defining the update interval in minutes */
     public static final IntegerProperty PROP_INTERVAL = new IntegerProperty("message.notifier.interval", 5);
-    
+
     private static final ScheduledExecutorService EXECUTOR = Executors.newSingleThreadScheduledExecutor();
-    
+
     private static final Runnable WORKER = new Worker();
-    
+
     private static ScheduledFuture<?> task = null;
-        
+
     private static class Worker implements Runnable {
 
@@ -82,5 +82,5 @@
         }
     }
-    
+
     /**
      * Starts the message notifier task if not already started and if user is fully identified
@@ -88,5 +88,7 @@
     public static void start() {
         int interval = PROP_INTERVAL.get();
-        if (!isRunning() && interval > 0 && isUserEnoughIdentified()) {
+        if (Main.isOffline(OnlineResource.OSM_API)) {
+            Main.info(tr("{0} not available (offline mode)", tr("Message notifier")));
+        } else if (!isRunning() && interval > 0 && isUserEnoughIdentified()) {
             task = EXECUTOR.scheduleAtFixedRate(WORKER, 0, interval * 60, TimeUnit.SECONDS);
             Main.info("Message notifier active (checks every "+interval+" minute"+(interval>1?"s":"")+")");
@@ -104,5 +106,5 @@
         }
     }
-    
+
     /**
      * Determines if the message notifier is currently running
@@ -112,5 +114,5 @@
         return task != null;
     }
-    
+
     /**
      * Determines if user set enough information in JOSM preferences to make the request to OSM API without
Index: /trunk/src/org/openstreetmap/josm/io/OfflineAccessException.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/OfflineAccessException.java	(revision 7434)
+++ /trunk/src/org/openstreetmap/josm/io/OfflineAccessException.java	(revision 7434)
@@ -0,0 +1,17 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.io;
+
+/**
+ * Exception thrown when an online resource is accessed while in offline mode.
+ * @since 7434
+ */
+public class OfflineAccessException extends IllegalStateException {
+
+    /**
+     * Constructs a new {@code OfflineAccessException}.
+     * @param s the String that contains a detailed message
+     */
+    public OfflineAccessException(String s) {
+        super(s);
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/io/OnlineResource.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/OnlineResource.java	(revision 7434)
+++ /trunk/src/org/openstreetmap/josm/io/OnlineResource.java	(revision 7434)
@@ -0,0 +1,50 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.io;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import org.openstreetmap.josm.Main;
+
+/**
+ * Online resources directly used by JOSM.
+ * This does not include websites where user can sometimes be redirected through its web browser,
+ * but only those to we establish a connection.
+ *
+ * @since 7434
+ */
+public enum OnlineResource {
+
+    /** The OSM API, used for download, upload, history, etc. */
+    OSM_API(tr("OSM API")),
+    /** The JOSM website, used for startup page, imagery/presets/styles/rules entries, help, etc. */
+    JOSM_WEBSITE(tr("JOSM website")),
+    /** Value used to represent all online resources */
+    ALL(tr("All"));
+
+    private final String locName;
+
+    private OnlineResource(String locName) {
+        this.locName = locName;
+    }
+
+    /**
+     * Replies the localized name.
+     * @return the localized name
+     */
+    public final String getLocName() {
+        return locName;
+    }
+
+    /**
+     * Ensures resource is not accessed in offline mode.
+     * @param downloadString The attempted download string
+     * @param resourceString The resource download string that should not be accessed
+     * @throws OfflineAccessException if resource is accessed in offline mode, in any protocol
+     */
+    public final void checkOfflineAccess(String downloadString, String resourceString) {
+        if (Main.isOffline(this) && downloadString.substring(downloadString.indexOf("://"))
+                .startsWith(resourceString.substring(resourceString.indexOf("://")))) {
+            throw new OfflineAccessException(tr("Unable to access ''{0}'': {1} not available (offline mode)", downloadString, getLocName()));
+        }
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/io/OsmApi.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/OsmApi.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/io/OsmApi.java	(revision 7434)
@@ -98,4 +98,8 @@
     }
 
+    private static String getServerUrlFromPref() {
+        return Main.pref.get("osm-server.url", DEFAULT_API_URL);
+    }
+
     /**
      * Replies the {@link OsmApi} for the URL given by the preference <code>osm-server.url</code>
@@ -104,6 +108,5 @@
      */
     public static OsmApi getOsmApi() {
-        String serverUrl = Main.pref.get("osm-server.url", DEFAULT_API_URL);
-        return getOsmApi(serverUrl);
+        return getOsmApi(getServerUrlFromPref());
     }
 
@@ -180,9 +183,11 @@
     private class CapabilitiesCache extends CacheCustomContent<OsmTransferException> {
 
+        private static final String CAPABILITIES = "capabilities";
+
         ProgressMonitor monitor;
         boolean fastFail;
 
         public CapabilitiesCache(ProgressMonitor monitor, boolean fastFail) {
-            super("capabilities" + getBaseUrl().hashCode(), CacheCustomContent.INTERVAL_WEEKLY);
+            super(CAPABILITIES + getBaseUrl().hashCode(), CacheCustomContent.INTERVAL_WEEKLY);
             this.monitor = monitor;
             this.fastFail = fastFail;
@@ -190,6 +195,11 @@
 
         @Override
+        protected void checkOfflineAccess() {
+            OnlineResource.OSM_API.checkOfflineAccess(getBaseUrl(getServerUrlFromPref(), "0.6")+CAPABILITIES, getServerUrlFromPref());
+        }
+
+        @Override
         protected byte[] updateData() throws OsmTransferException {
-            return sendRequest("GET", "capabilities", null, monitor, false, fastFail).getBytes(StandardCharsets.UTF_8);
+            return sendRequest("GET", CAPABILITIES, null, monitor, false, fastFail).getBytes(StandardCharsets.UTF_8);
         }
     }
@@ -217,4 +227,8 @@
         if (initialized)
             return;
+        if (Main.isOffline(OnlineResource.OSM_API)) {
+            // At this point this is an error because all automatic or UI actions requiring OSM API should have been disabled earlier
+            throw new OfflineAccessException(tr("{0} not available (offline mode)", OnlineResource.OSM_API.getLocName()));
+        }
         cancel = false;
         try {
@@ -324,9 +338,5 @@
     }
 
-    /**
-     * Returns the base URL for API requests, including the negotiated version number.
-     * @return base URL string
-     */
-    public String getBaseUrl() {
+    private static String getBaseUrl(String serverUrl, String version) {
         StringBuilder rv = new StringBuilder(serverUrl);
         if (version != null) {
@@ -339,4 +349,12 @@
         int p; while ((p = rv.indexOf("//", rv.indexOf("://")+2)) > -1) { rv.delete(p, p + 1); }
         return rv.toString();
+    }
+
+    /**
+     * Returns the base URL for API requests, including the negotiated version number.
+     * @return base URL string
+     */
+    public String getBaseUrl() {
+        return getBaseUrl(serverUrl, version);
     }
 
Index: /trunk/src/org/openstreetmap/josm/io/OsmServerReader.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/OsmServerReader.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/io/OsmServerReader.java	(revision 7434)
@@ -118,4 +118,7 @@
     protected InputStream getInputStreamRaw(String urlStr, ProgressMonitor progressMonitor, String reason, boolean uncompressAccordingToContentDisposition) throws OsmTransferException {
         try {
+            OnlineResource.JOSM_WEBSITE.checkOfflineAccess(urlStr, Main.getJOSMWebsite());
+            OnlineResource.OSM_API.checkOfflineAccess(urlStr, Main.pref.get("osm-server.url", OsmApi.DEFAULT_API_URL));
+
             URL url = null;
             try {
Index: /trunk/src/org/openstreetmap/josm/plugins/PluginHandler.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/plugins/PluginHandler.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/plugins/PluginHandler.java	(revision 7434)
@@ -63,4 +63,6 @@
 import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
 import org.openstreetmap.josm.gui.widgets.JosmTextArea;
+import org.openstreetmap.josm.io.OfflineAccessException;
+import org.openstreetmap.josm.io.OnlineResource;
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.I18n;
@@ -303,4 +305,8 @@
      */
     public static boolean checkAndConfirmPluginUpdate(Component parent) {
+        if (!checkOfflineAccess()) {
+            Main.info(tr("{0} not available (offline mode)", tr("Plugin update")));
+            return false;
+        }
         String message = null;
         String togglePreferenceKey = null;
@@ -403,4 +409,23 @@
         }
         return ret == 0;
+    }
+
+    private static boolean checkOfflineAccess() {
+        if (Main.isOffline(OnlineResource.ALL)) {
+            return false;
+        }
+        if (Main.isOffline(OnlineResource.JOSM_WEBSITE)) {
+            for (String updateSite : Main.pref.getPluginSites()) {
+                try {
+                    OnlineResource.JOSM_WEBSITE.checkOfflineAccess(updateSite, Main.getJOSMWebsite());
+                } catch (OfflineAccessException e) {
+                    if (Main.isTraceEnabled()) {
+                        Main.trace(e.getMessage());
+                    }
+                    return false;
+                }
+            }
+        }
+        return true;
     }
 
Index: /trunk/src/org/openstreetmap/josm/plugins/ReadRemotePluginInformationTask.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/plugins/ReadRemotePluginInformationTask.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/plugins/ReadRemotePluginInformationTask.java	(revision 7434)
@@ -60,5 +60,5 @@
     protected enum CacheType {PLUGIN_LIST, ICON_LIST}
 
-    protected final void init(Collection<String> sites, boolean displayErrMsg){
+    protected final void init(Collection<String> sites, boolean displayErrMsg) {
         this.sites = sites;
         if (sites == null) {
@@ -68,6 +68,7 @@
         this.displayErrMsg = displayErrMsg;
     }
-    /**
-     * Creates the task
+
+    /**
+     * Constructs a new {@code ReadRemotePluginInformationTask}.
      *
      * @param sites the collection of download sites. Defaults to the empty collection if null.
@@ -79,5 +80,5 @@
 
     /**
-     * Creates the task
+     * Constructs a new {@code ReadRemotePluginInformationTask}.
      *
      * @param monitor the progress monitor. Defaults to {@link NullProgressMonitor#INSTANCE} if null
@@ -104,6 +105,5 @@
 
     /**
-     * Creates the file name for the cached plugin list and the icon cache
-     * file.
+     * Creates the file name for the cached plugin list and the icon cache file.
      *
      * @param site the name of the site
@@ -403,6 +403,5 @@
             siteCacheFiles.remove(createSiteCacheFile(pluginDir, site, CacheType.PLUGIN_LIST));
             siteCacheFiles.remove(createSiteCacheFile(pluginDir, site, CacheType.ICON_LIST));
-            if(list != null)
-            {
+            if (list != null) {
                 getProgressMonitor().worked(1);
                 cachePluginList(site, list);
@@ -416,6 +415,6 @@
             downloadPluginIcons(site+"-icons.zip", createSiteCacheFile(pluginDir, site, CacheType.ICON_LIST), getProgressMonitor().createSubTaskMonitor(0, false));
         }
-        for (File file: siteCacheFiles) /* remove old stuff or whole update process is broken */
-        {
+        // remove old stuff or whole update process is broken
+        for (File file: siteCacheFiles) {
             file.delete();
         }
Index: /trunk/src/org/openstreetmap/josm/tools/ExceptionUtil.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/tools/ExceptionUtil.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/tools/ExceptionUtil.java	(revision 7434)
@@ -28,4 +28,5 @@
 import org.openstreetmap.josm.io.IllegalDataException;
 import org.openstreetmap.josm.io.MissingOAuthAccessTokenException;
+import org.openstreetmap.josm.io.OfflineAccessException;
 import org.openstreetmap.josm.io.OsmApi;
 import org.openstreetmap.josm.io.OsmApiException;
@@ -496,4 +497,19 @@
 
     /**
+     * Explains a {@link OfflineAccessException} which has caused an {@link OsmTransferException}.
+     * This is most likely happening when JOSM tries to access OSM API or JOSM website while in offline mode.
+     *
+     * @param e the exception
+     * @return The HTML formatted error message to display
+     * @since 7434
+     */
+    public static String explainOfflineAccessException(OsmTransferException e) {
+        OfflineAccessException oae = getNestedException(e, OfflineAccessException.class);
+        Main.error(e);
+        return tr("<html>Failed to download data.<br>"
+                + "<br>Details: {0}</html>", oae.getMessage());
+    }
+
+    /**
      * Explains a {@link OsmApiException} which was thrown because of an internal server
      * error in the OSM API server..
Index: /trunk/src/org/openstreetmap/josm/tools/WikiReader.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/tools/WikiReader.java	(revision 7433)
+++ /trunk/src/org/openstreetmap/josm/tools/WikiReader.java	(revision 7434)
@@ -30,5 +30,14 @@
      */
     public WikiReader() {
-        this.baseurl = Main.pref.get("help.baseurl", Main.getJOSMWebsite());
+        this(Main.pref.get("help.baseurl", Main.getJOSMWebsite()));
+    }
+
+    /**
+     * Returns the base URL of wiki.
+     * @return the base URL of wiki
+     * @since 7434
+     */
+    public final String getBaseUrlWiki() {
+        return baseurl + "/wiki/";
     }
 
@@ -46,5 +55,5 @@
         try (BufferedReader in = Utils.openURLReader(u)) {
             boolean txt = url.endsWith("?format=txt");
-            if (url.startsWith(baseurl) && !txt)
+            if (url.startsWith(getBaseUrlWiki()) && !txt)
                 return readFromTrac(in, u);
             return readNormal(in, !txt);
@@ -64,5 +73,5 @@
         languageCode = LanguageInfo.getWikiLanguagePrefix(LocaleType.DEFAULTNOTENGLISH);
         if(languageCode != null) {
-            res = readLang(new URL(baseurl + "/wiki/" + languageCode + text));
+            res = readLang(new URL(getBaseUrlWiki() + languageCode + text));
         }
 
@@ -70,5 +79,5 @@
             languageCode = LanguageInfo.getWikiLanguagePrefix(LocaleType.BASELANGUAGE);
             if(languageCode != null) {
-                res = readLang(new URL(baseurl + "/wiki/" + languageCode + text));
+                res = readLang(new URL(getBaseUrlWiki() + languageCode + text));
             }
         }
@@ -77,5 +86,5 @@
             languageCode = LanguageInfo.getWikiLanguagePrefix(LocaleType.ENGLISH);
             if(languageCode != null) {
-                res = readLang(new URL(baseurl + "/wiki/" + languageCode + text));
+                res = readLang(new URL(getBaseUrlWiki() + languageCode + text));
             }
         }
