diff --git a/css/app.css b/css/app.css
index ad669a57a..9317d70d3 100644
--- a/css/app.css
+++ b/css/app.css
@@ -1874,6 +1874,18 @@ img.wiki-image {
content: ', ';
}
+/* API Status */
+
+.api-status {
+ float: left;
+}
+
+.api-status.offline,
+.api-status.readonly {
+ background: red;
+ padding: 5px 10px;
+}
+
/* Account Information */
.account {
diff --git a/data/core.yaml b/data/core.yaml
index 3823a238b..457528b8c 100644
--- a/data/core.yaml
+++ b/data/core.yaml
@@ -129,6 +129,10 @@ en:
logout: logout
loading_auth: "Connecting to OpenStreetMap..."
report_a_bug: report a bug
+ status:
+ error: Unable to connect to API.
+ offline: The API is offline. Please try editing later.
+ readonly: The API is read-only. You will need to wait to save your changes.
commit:
title: Save Changes
description_placeholder: Brief description of your contributions
diff --git a/data/locales/en.js b/data/locales/en.js
index 9e4656df9..efbf2f3f5 100644
--- a/data/locales/en.js
+++ b/data/locales/en.js
@@ -163,6 +163,11 @@ locale.en = {
"logout": "logout",
"loading_auth": "Connecting to OpenStreetMap...",
"report_a_bug": "report a bug",
+ "status": {
+ "error": "Unable to connect to API.",
+ "offline": "The API is offline. Please try editing later.",
+ "readonly": "The API is read-only. You will need to wait to save your changes."
+ },
"commit": {
"title": "Save Changes",
"description_placeholder": "Brief description of your contributions",
diff --git a/index.html b/index.html
index c8dbaf254..cb722a733 100644
--- a/index.html
+++ b/index.html
@@ -87,6 +87,7 @@
+
diff --git a/js/id/connection.js b/js/id/connection.js
index d7b9e51a0..c4312a932 100644
--- a/js/id/connection.js
+++ b/js/id/connection.js
@@ -239,6 +239,15 @@ iD.Connection = function(context) {
oauth.xhr({ method: 'GET', path: '/api/0.6/user/details' }, done);
};
+ connection.status = function(callback) {
+ function done(err, capabilities) {
+ if (err) return callback(err);
+ var apiStatus = capabilities.getElementsByTagName('status');
+ callback(undefined, apiStatus[0].getAttribute('api'));
+ }
+ oauth.xhr({ method: 'GET', path: '/api/capabilities' }, done);
+ };
+
function abortRequest(i) { i.abort(); }
connection.loadTiles = function(projection, dimensions) {
diff --git a/js/id/ui.js b/js/id/ui.js
index 85e9523da..99555ed99 100644
--- a/js/id/ui.js
+++ b/js/id/ui.js
@@ -78,6 +78,10 @@ iD.ui = function(context) {
var about = container.append('div')
.attr('class','col12 about-block fillD');
+ about.append('div')
+ .attr('class', 'api-status')
+ .call(iD.ui.Status(context));
+
if (!context.embed()) {
about.append('div')
.attr('class', 'account')
diff --git a/js/id/ui/status.js b/js/id/ui/status.js
new file mode 100644
index 000000000..81702dab9
--- /dev/null
+++ b/js/id/ui/status.js
@@ -0,0 +1,35 @@
+iD.ui.Status = function(context) {
+ var connection = context.connection(),
+ errCount = 0;
+
+ return function(selection) {
+
+ function update() {
+
+ connection.status(function(err, apiStatus) {
+
+ selection.html('');
+
+ if (err && errCount++ < 2) return;
+
+ if (err) {
+ selection.text(t('status.error'));
+
+ } else if (apiStatus === 'readonly') {
+ selection.text(t('status.readonly'));
+
+ } else if (apiStatus === 'offline') {
+ selection.text(t('status.offline'));
+ }
+
+ selection.attr('class', 'api-status ' + (err ? 'error' : apiStatus));
+ if (!err) errCount = 0;
+
+ });
+ }
+
+ connection.on('auth', function() { update(selection); });
+ window.setInterval(update, 90000);
+ update(selection);
+ };
+};