Update iD; clean up OAuth configuration
authorJohn Firebaugh <john.firebaugh@gmail.com>
Wed, 17 Apr 2013 23:36:09 +0000 (16:36 -0700)
committerJohn Firebaugh <john.firebaugh@gmail.com>
Fri, 3 May 2013 17:52:42 +0000 (10:52 -0700)
app/views/site/_id.html.erb
app/views/site/id_iframe.html.erb
vendor/assets/iD/iD.js

index 680c45cdebd1f2707662eb714ae7ad70caaa1474..d94f871b7541ddeb389b85284be0922dceb084ab 100644 (file)
@@ -1,34 +1,19 @@
 <% if defined? ID_KEY %>
-<iframe frameBorder="0" id="id-embed" class="id-embed"></iframe>
-<% token = @user.access_token(ID_KEY) %>
-<script>
-var keys = {
-    '<%= request.protocol + request.host_with_port %>': {
-        oauth_consumer_key: "<%= token.client_application.key %>",
-        oauth_secret: "<%= token.client_application.secret %>",
-        oauth_token: "<%= token.token %>",
-        oauth_token_secret: "<%= token.secret %>"
-    }
-};
-
-var coord = {};
-<% if @lat && @lon && @zoom -%>
-coord.lat = <%= @lat %>;
-coord.lon = <%= @lon %>;
-coord.zoom = <%= @zoom %>;
-<% else -%>
-var params = OSM.mapParams();
-coord.lat = params.lat;
-coord.lon = params.lon;
-coord.zoom = params.zoom;
-<% end -%>
-var url = 'id_iframe#map=' + coord.zoom + '/' + coord.lon + '/' + coord.lat +
-    '&preauth=' + JSON.stringify(keys);
-$('#id-embed').attr('src', url);
-</script>
-<% else%>
-<script type="text/javascript">alert("<%= t 'site.edit.id_not_configured' %>")</script>
+  <iframe frameBorder="0" id="id-embed" class="id-embed"></iframe>
+  <script>
+    var coord = {};
+    <% if @lat && @lon && @zoom -%>
+    coord.lat = <%= @lat %>;
+    coord.lon = <%= @lon %>;
+    coord.zoom = <%= @zoom %>;
+    <% else -%>
+    var params = OSM.mapParams();
+    coord.lat = params.lat;
+    coord.lon = params.lon;
+    coord.zoom = params.zoom;
+    <% end -%>
+    $('#id-embed').attr('src', 'id_iframe#map=' + coord.zoom + '/' + coord.lon + '/' + coord.lat);
+  </script>
+<% else %>
+  <script type="text/javascript">alert("<%= t 'site.edit.id_not_configured' %>")</script>
 <% end %>
-
-<script type="text/javascript">
-</script>
index 0422446b34390199cc56711fddbaf3db702ae885..5c061ea3a40e42094bae78834e921a47d1cc17eb 100644 (file)
       'Please upgrade your browser or use Potlatch 2 to edit the map.';
     document.getElementById('id-container').className = 'unsupported';
   } else {
-    var qs = iD.util.stringQs(location.hash);
-    if (qs.preauth) {
-      var preauth = JSON.parse(qs.preauth);
+    var id = iD()
+      .embed(true)
+      .imagePath("/assets/iD/img/")<%# Can't use asset_path('id/img/') in production. %>
+      .preauth({
+        <% token = @user.access_token(ID_KEY) %>
+        url: "<%= request.protocol + request.host_with_port %>",
+        oauth_consumer_key: "<%= token.client_application.key %>",
+        oauth_secret: "<%= token.client_application.secret %>",
+        oauth_token: "<%= token.token %>",
+        oauth_token_secret: "<%= token.secret %>"
+      });
 
-      var id = iD()
-        .embed(true)
-        .imagePath("/assets/iD/img/"); <%# Can't use asset_path('id/img/') in production. %>
-
-      id.connection()
-        .url(Object.keys(preauth)[0])
-        .keys(preauth);
-
-      d3.select('#id-container')
-        .call(id.ui());
-    }
+    d3.select('#id-container')
+      .call(id.ui());
   }
 </script>
 </body>
index 11dd2f7b04ff398119a639b1af75ba78de867c79..6adb5fef09610704e3326cf7939b82b6c582bafd 100644 (file)
@@ -10729,7 +10729,7 @@ var JXON = new (function () {
   }
 }(this));
 (function(e){if("function"==typeof bootstrap)bootstrap("osmauth",e);else if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else if("undefined"!=typeof ses){if(!ses.ok())return;ses.makeOsmAuth=e}else"undefined"!=typeof window?window.osmAuth=e():global.osmAuth=e()})(function(){var define,ses,bootstrap,module,exports;
-return (function(e,t,n){function r(n,i){if(!t[n]){if(!e[n]){var s=typeof require=="function"&&require;if(!i&&s)return s(n,!0);throw new Error("Cannot find module '"+n+"'")}var o=t[n]={exports:{}};e[n][0](function(t){var i=e[n][1][t];return r(i?i:t)},o,o.exports)}return t[n].exports}for(var i=0;i<n.length;i++)r(n[i]);return r})({1:[function(require,module,exports){
+return (function(e,t,n){function i(n,s){if(!t[n]){if(!e[n]){var o=typeof require=="function"&&require;if(!s&&o)return o(n,!0);if(r)return r(n,!0);throw new Error("Cannot find module '"+n+"'")}var u=t[n]={exports:{}};e[n][0].call(u.exports,function(t){var r=e[n][1][t];return i(r?r:t)},u,u.exports)}return t[n].exports}var r=typeof require=="function"&&require;for(var s=0;s<n.length;s++)i(n[s]);return i})({1:[function(require,module,exports){
 var ohauth = require('ohauth'),
     store = require('store');
 
@@ -10738,24 +10738,10 @@ var ohauth = require('ohauth'),
 // This code is only compatible with IE10+ because the [XDomainRequest](http://bit.ly/LfO7xo)
 // object, IE<10's idea of [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing),
 // does not support custom headers, which this uses everywhere.
-module.exports = function(keys, o) {
+module.exports = function(o) {
 
     var oauth = {};
 
-    // keys is for keys. for example,
-    //
-    //     { "http://www.openstreetmap.org/": {
-    //         oauth_secret: '9WfJnwQxDvvYagx1Ut0tZBsOZ0ZCzAvOje3u1TV0',
-    //         oauth_consumer_key: "WLwXbm6XFMG7WrVnE8enIF6GzyefYIN6oUJSxG65"
-    //     } }
-    o = o || {};
-    o.url = o.url || 'http://www.openstreetmap.org';
-
-    // Optional loading and loading-done functions for nice UI feedback.
-    // by default, no-ops
-    o.loading = o.loading || function() {};
-    o.done = o.done || function() {};
-
     // authenticated users will also have a request token secret, but it's
     // not used in transactions with the server
     oauth.authenticated = function() {
@@ -10780,7 +10766,7 @@ module.exports = function(keys, o) {
             url = o.url + '/oauth/request_token';
 
         params.oauth_signature = ohauth.signature(
-            keys[o.url].oauth_secret, '',
+            o.oauth_secret, '',
             ohauth.baseString('POST', url, params));
 
         // Create a 600x550 popup window in the center of the screen
@@ -10806,11 +10792,11 @@ module.exports = function(keys, o) {
             popup.location = o.url + '/oauth/authorize?' + ohauth.qsString({
                 oauth_token: resp.oauth_token,
                 oauth_callback: location.href.replace('index.html', '')
-                    .replace(/#.+/, '') + 'land.html'
+                    .replace(/#.+/, '') + o.landing
             });
         }
 
-        // Called by a function in `land.html`, in the popup window. The
+        // Called by a function in a landing page, in the popup window. The
         // window closes itself.
         window.authComplete = function(token) {
             var oauth_token = ohauth.stringQs(token.split('?')[1]);
@@ -10821,14 +10807,14 @@ module.exports = function(keys, o) {
         // ## Getting an request token
         //
         // At this point we have an `oauth_token`, brought in from a function
-        // call on the `landing.html` popup.
+        // call on a landing page popup.
         function get_access_token(oauth_token) {
             var url = o.url + '/oauth/access_token',
                 params = timenonce(getAuth(o)),
                 request_token_secret = token('oauth_request_token_secret');
             params.oauth_token = oauth_token;
             params.oauth_signature = ohauth.signature(
-                keys[o.url].oauth_secret,
+                o.oauth_secret,
                 request_token_secret,
                 ohauth.baseString('POST', url, params));
 
@@ -10867,7 +10853,7 @@ module.exports = function(keys, o) {
 
             params.oauth_token = token('oauth_token');
             params.oauth_signature = ohauth.signature(
-                keys[o.url].oauth_secret,
+                o.oauth_secret,
                 oauth_token_secret,
                 ohauth.baseString(options.method, url, params));
 
@@ -10882,28 +10868,29 @@ module.exports = function(keys, o) {
         }
     };
 
-    // Reset the base URL that this OAuth connection points to
-    oauth.url = function(_) {
-        if (!arguments.length) return o.url;
-        o.url = _;
-        return oauth;
-    };
-
     // pre-authorize this object, if we can just get a token and token_secret
     // from the start
-    oauth.preauth = function(_) {
-        var c = _[o.url];
+    oauth.preauth = function(c) {
         if (!c) return;
         if (c.oauth_token) token('oauth_token', c.oauth_token);
         if (c.oauth_token_secret) token('oauth_token_secret', c.oauth_token_secret);
         return oauth;
     };
 
-    // Reset the base URL that this OAuth connection points to
-    oauth.keys = function(_) {
-        if (!arguments.length) return keys;
-        keys = _;
-        return oauth.preauth(keys);
+    oauth.options = function(_) {
+        if (!arguments.length) return o;
+
+        o = _;
+
+        o.url = o.url || 'http://www.openstreetmap.org';
+        o.landing = o.landing || 'land.html';
+
+        // Optional loading and loading-done functions for nice UI feedback.
+        // by default, no-ops
+        o.loading = o.loading || function() {};
+        o.done = o.done || function() {};
+
+        return oauth.preauth(o);
     };
 
     // 'stamp' an authentication object from `getAuth()`
@@ -10928,18 +10915,18 @@ module.exports = function(keys, o) {
     // it doesn't contain undesired properties for authentication
     function getAuth(o) {
         return {
-            oauth_consumer_key: keys[o.url].oauth_consumer_key,
+            oauth_consumer_key: o.oauth_consumer_key,
             oauth_signature_method: "HMAC-SHA1"
         };
     }
 
     // potentially pre-authorize
-    oauth.keys(keys);
+    oauth.options(o);
 
     return oauth;
 };
 
-},{"ohauth":2,"store":3}],3:[function(require,module,exports){
+},{"store":2,"ohauth":3}],2:[function(require,module,exports){
 /* Copyright (c) 2010-2012 Marcus Westin
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -11116,7 +11103,9 @@ module.exports = function(keys, o) {
        else { this.store = store }
 })();
 
-},{}],2:[function(require,module,exports){
+},{}],3:[function(require,module,exports){
+'use strict';
+
 var hashes = require('jshashes'),
     sha1 = new hashes.SHA1();
 
@@ -11139,7 +11128,8 @@ ohauth.stringQs = function(str) {
 };
 
 ohauth.rawxhr = function(method, url, data, headers, callback) {
-    var xhr = new XMLHttpRequest(), twoHundred = /^20\d$/;
+    var xhr = new XMLHttpRequest(),
+        twoHundred = /^20\d$/;
     xhr.onreadystatechange = function() {
         if (4 == xhr.readyState && 0 !== xhr.status) {
             if (twoHundred.test(xhr.status)) callback(null, xhr);
@@ -11200,20 +11190,20 @@ module.exports = ohauth;
 
 },{"jshashes":4}],4:[function(require,module,exports){
 (function(global){/**\r
- * jsHashes - A fast and independent hashing library pure JavaScript implemented for both server and client side\r
+ * jsHashes - A fast and independent hashing library pure JavaScript implemented (ES5 compliant) for both server and client side\r
  * \r
  * @class Hashes\r
  * @author Tomas Aparicio <tomas@rijndael-project.com>\r
  * @license New BSD (see LICENSE file)\r
- * @version 1.0.1 - 17/02/2013\r
+ * @version 1.0.3\r
  *\r
  * Algorithms specification:\r
  *\r
  * MD5 <http://www.ietf.org/rfc/rfc1321.txt>\r
  * RIPEMD-160 <http://homes.esat.kuleuven.be/~bosselae/ripemd160.html>\r
- * SHA1 <http://homes.esat.kuleuven.be/~bosselae/ripemd160.html>\r
- * SHA256 <http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf>\r
- * SHA512 <http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf>\r
+ * SHA1   <http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf>\r
+ * SHA256 <http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf>\r
+ * SHA512 <http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf>\r
  * HMAC <http://www.ietf.org/rfc/rfc2104.txt>\r
  *\r
  */\r
@@ -11222,11 +11212,11 @@ module.exports = ohauth;
   \r
   // private helper methods\r
   function utf8Encode(input) {\r
-    var output = '', i = -1, x, y;\r
-    while (++i < input.length) {\r
+    var  x, y, output = '', i = -1, l = input.length;\r
+    while ((i+=1) < l) {\r
       /* Decode utf-16 surrogate pairs */\r
       x = input.charCodeAt(i);\r
-      y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;\r
+      y = i + 1 < l ? input.charCodeAt(i + 1) : 0;\r
       if (0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) {\r
           x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);\r
           i += 1;\r
@@ -11252,11 +11242,11 @@ module.exports = ohauth;
   }\r
   \r
   function utf8Decode(str_data) {\r
-    var i, ac, c1, c2, c3, arr = [];\r
+    var i, ac, c1, c2, c3, arr = [], l = str_data.length;\r
     i = ac = c1 = c2 = c3 = 0;\r
     str_data += '';\r
 \r
-    while (i < str_data.length) {\r
+    while (i < l) {\r
         c1 = str_data.charCodeAt(i);\r
         ac += 1;\r
         if (c1 < 128) {\r
@@ -11298,8 +11288,8 @@ module.exports = ohauth;
    */\r
   function rstr2hex(input, hexcase) {\r
     var hex_tab = hexcase ? '0123456789ABCDEF' : '0123456789abcdef',\r
-        output = '', x, i = 0;\r
-    for (; i < input.length; i+=1) {\r
+        output = '', x, i = 0, l = input.length;\r
+    for (; i < l; i+=1) {\r
       x = input.charCodeAt(i);\r
       output += hex_tab.charAt((x >>> 4) & 0x0F) + hex_tab.charAt(x & 0x0F);\r
     }\r
@@ -11310,16 +11300,16 @@ module.exports = ohauth;
    * Encode a string as utf-16\r
    */\r
   function str2rstr_utf16le(input) {\r
-    var i = 0, output = '';\r
-    for (; i < input.length; i+=1) {\r
+    var i, l = input.length, output = '';\r
+    for (i = 0; i < l; i+=1) {\r
       output += String.fromCharCode( input.charCodeAt(i) & 0xFF, (input.charCodeAt(i) >>> 8) & 0xFF);\r
     }\r
     return output;\r
   }\r
 \r
   function str2rstr_utf16be(input) {\r
-    var i = 0, output = '';\r
-    for (; i < input.length; i+=1) {\r
+    var i, l = input.length, output = '';\r
+    for (i = 0; i < l; i+=1) {\r
       output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF, input.charCodeAt(i) & 0xFF);\r
     }\r
     return output;\r
@@ -11329,8 +11319,8 @@ module.exports = ohauth;
    * Convert an array of big-endian words to a string\r
    */\r
   function binb2rstr(input) {\r
-    var i = 0, output = '';\r
-    for (;i < input.length * 32; i += 8) {\r
+    var i, l = input.length * 32, output = '';\r
+    for (i = 0; i < l; i += 8) {\r
         output += String.fromCharCode((input[i>>5] >>> (24 - i % 32)) & 0xFF);\r
     }\r
     return output;\r
@@ -11340,8 +11330,8 @@ module.exports = ohauth;
    * Convert an array of little-endian words to a string\r
    */\r
   function binl2rstr(input) {\r
-    var i = 0, output = '';\r
-    for (;i < input.length * 32; i += 8) {\r
+    var i, l = input.length * 32, output = '';\r
+    for (i = 0;i < l; i += 8) {\r
       output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF);\r
     }\r
     return output;\r
@@ -11352,11 +11342,11 @@ module.exports = ohauth;
    * Characters >255 have their high-byte silently ignored.\r
    */\r
   function rstr2binl(input) {\r
-    var i, output = Array(input.length >> 2);\r
-    for (i = 0; i < output.length; i+=1) {\r
+    var i, l = input.length * 8, output = Array(input.length >> 2), lo = output.length;\r
+    for (i = 0; i < lo; i+=1) {\r
       output[i] = 0;\r
     }\r
-    for (i = 0; i < input.length * 8; i += 8) {\r
+    for (i = 0; i < l; i += 8) {\r
       output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (i%32);\r
     }\r
     return output;\r
@@ -11367,11 +11357,11 @@ module.exports = ohauth;
    * Characters >255 have their high-byte silently ignored.\r
    */\r
    function rstr2binb(input) {\r
-      var i, output = Array(input.length >> 2);\r
-      for (i = 0; i < output.length; i+=1) {\r
+      var i, l = input.length * 8, output = Array(input.length >> 2), lo = output.length;\r
+      for (i = 0; i < lo; i+=1) {\r
             output[i] = 0;\r
         }\r
-      for (i = 0; i < input.length * 8; i += 8) {\r
+      for (i = 0; i < l; i += 8) {\r
             output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);\r
         }\r
       return output;\r
@@ -11383,11 +11373,12 @@ module.exports = ohauth;
   function rstr2any(input, encoding) {\r
     var divisor = encoding.length,\r
         remainders = Array(),\r
-        i, q, x, quotient, dividend, output, full_length;\r
+        i, q, x, ld, quotient, dividend, output, full_length;\r
   \r
     /* Convert to an array of 16-bit big-endian values, forming the dividend */\r
     dividend = Array(Math.ceil(input.length / 2));\r
-    for (i = 0; i < dividend.length; i+=1) {\r
+    ld = dividend.length;\r
+    for (i = 0; i < ld; i+=1) {\r
       dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);\r
     }\r
   \r
@@ -11438,7 +11429,7 @@ module.exports = ohauth;
       triplet = (input.charCodeAt(i) << 16)\r
             | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)\r
             | (i + 2 < len ? input.charCodeAt(i+2)      : 0);\r
-      for (j = 0; j < 4; j++) {\r
+      for (j = 0; j < 4; j+=1) {\r
         if (i * 8 + j * 6 > input.length * 8) { \r
           output += b64pad; \r
         } else { \r
@@ -11450,6 +11441,11 @@ module.exports = ohauth;
   }\r
 \r
   Hashes = {\r
+  /**  \r
+   * @property {String} version\r
+   * @readonly\r
+   */\r
+  VERSION : '1.0.3',\r
   /**\r
    * @member Hashes\r
    * @class Base64\r
@@ -11475,8 +11471,8 @@ module.exports = ohauth;
         triplet = (input.charCodeAt(i) << 16)\r
               | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)\r
               | (i + 2 < len ? input.charCodeAt(i+2) : 0);\r
-        for (j = 0; j < 4; j++) {\r
-          if (i * 8 + j * 6 > input.length * 8) {\r
+        for (j = 0; j < 4; j+=1) {\r
+          if (i * 8 + j * 6 > len * 8) {\r
               output += pad;\r
           } else {\r
               output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);\r
@@ -11553,35 +11549,37 @@ module.exports = ohauth;
    * @return {String}\r
    */\r
   CRC32 : function (str) {\r
-    var crc = 0, x = 0, y = 0, table, i;\r
+    var crc = 0, x = 0, y = 0, table, i, iTop;\r
     str = utf8Encode(str);\r
         \r
-    table = '00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 ' +\r
-            '79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 ' +\r
-            '84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F ' +\r
-            '63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD ' +\r
-            'A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC ' +\r
-            '51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 ' +\r
-            'B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 ' +\r
-            '06B6B51F 9FBFE4A5 E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 ' +\r
-            'E6635C01 6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 ' +\r
-            '12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE A3BC0074 ' +\r
-            'D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 ' +\r
-            '33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 ' +\r
-            'CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 ' +\r
-            '9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E ' +\r
-            '7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D ' +\r
-            '806567CB 196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 ' +\r
-            '60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA ' +\r
-            'AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 ' + \r
-            '5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 ' +\r
-            'B5D0CF31 2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 ' +\r
-            '05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 ' +\r
-            'F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 88085AE6 FF0F6A70 66063BCA ' +\r
-            '11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 ' +\r
-            'D06016F7 4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F ' +\r
-            '30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E ' +\r
-            'C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D';\r
+    table = [ \r
+        '00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 ',\r
+        '79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 ',\r
+        '84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F ',\r
+        '63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD ',\r
+        'A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC ',\r
+        '51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 ',\r
+        'B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 ',\r
+        '06B6B51F 9FBFE4A5 E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 ',\r
+        'E6635C01 6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 ',\r
+        '12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE A3BC0074 ',\r
+        'D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 ',\r
+        '33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 ',\r
+        'CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 ',\r
+        '9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E ',\r
+        '7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D ',\r
+        '806567CB 196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 ',\r
+        '60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA ',\r
+        'AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 ', \r
+        '5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 ',\r
+        'B5D0CF31 2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 ',\r
+        '05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 ',\r
+        'F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 88085AE6 FF0F6A70 66063BCA ',\r
+        '11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 ',\r
+        'D06016F7 4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F ',\r
+        '30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E ',\r
+        'C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D'\r
+    ].join('');\r
 \r
     crc = crc ^ (-1);\r
     for (i = 0, iTop = str.length; i < iTop; i+=1 ) {\r
@@ -11589,7 +11587,8 @@ module.exports = ohauth;
         x = '0x' + table.substr( y * 9, 8 );\r
         crc = ( crc >>> 8 ) ^ x;\r
     }\r
-    return crc ^ (-1);\r
+    // always return a positive number (that's what >>> 0 does)\r
+    return (crc ^ (-1)) >>> 0;\r
   },\r
   /**\r
    * @member Hashes\r
@@ -11685,7 +11684,7 @@ module.exports = ohauth;
      * Calculate the HMAC-MD5, of a key and some data (raw strings)\r
      */\r
     function rstr_hmac(key, data) {\r
-      var bkey, ipad, hash, i;\r
+      var bkey, ipad, opad, hash, i;\r
 \r
       key = (utf8) ? utf8Encode(key) : key;\r
       data = (utf8) ? utf8Encode(data) : data;\r
@@ -11915,7 +11914,7 @@ module.exports = ohauth;
      * Calculate the HMAC-SHA1 of a key and some data (raw strings)\r
      */\r
     function rstr_hmac(key, data) {\r
-      var bkey, ipad, i, hash;\r
+       var bkey, ipad, opad, i, hash;\r
        key = (utf8) ? utf8Encode(key) : key;\r
        data = (utf8) ? utf8Encode(data) : data;\r
        bkey = rstr2binb(key);\r
@@ -11955,7 +11954,7 @@ module.exports = ohauth;
         oldd = d;\r
         olde = e;\r
       \r
-       for (j = 0; j < 80; j++)        {\r
+       for (j = 0; j < 80; j+=1)       {\r
          if (j < 16) { \r
             w[j] = x[i + j]; \r
           } else { \r
@@ -12130,8 +12129,7 @@ module.exports = ohauth;
     function sha256_Gamma0512(x) {return (sha256_S(x, 1)  ^ sha256_S(x, 8) ^ sha256_R(x, 7));}\r
     function sha256_Gamma1512(x) {return (sha256_S(x, 19) ^ sha256_S(x, 61) ^ sha256_R(x, 6));}\r
     \r
-    sha256_K = new Array\r
-    (\r
+    sha256_K = [\r
       1116352408, 1899447441, -1245643825, -373957723, 961987163, 1508970993,\r
       -1841331548, -1424204075, -670586216, 310598401, 607225278, 1426881987,\r
       1925078388, -2132889090, -1680079193, -1046744716, -459576895, -272742522,\r
@@ -12143,11 +12141,11 @@ module.exports = ohauth;
       430227734, 506948616, 659060556, 883997877, 958139571, 1322822218,\r
       1537002063, 1747873779, 1955562222, 2024104815, -2067236844, -1933114872,\r
       -1866530822, -1538233109, -1090935817, -965641998\r
-    );\r
+    ];\r
     \r
     function binb(m, l) {\r
-      var HASH = new Array(1779033703, -1150833019, 1013904242, -1521486534,\r
-                 1359893119, -1694144372, 528734635, 1541459225);\r
+      var HASH = [1779033703, -1150833019, 1013904242, -1521486534,\r
+                 1359893119, -1694144372, 528734635, 1541459225];\r
       var W = new Array(64);\r
       var a, b, c, d, e, f, g, h;\r
       var i, j, T1, T2;\r
@@ -12167,7 +12165,7 @@ module.exports = ohauth;
       g = HASH[6];\r
       h = HASH[7];\r
     \r
-      for (j = 0; j < 64; j++)\r
+      for (j = 0; j < 64; j+=1)\r
       {\r
         if (j < 16) { \r
           W[j] = m[j + i];\r
@@ -12321,11 +12319,11 @@ module.exports = ohauth;
      * Calculate the SHA-512 of an array of big-endian dwords, and a bit length\r
      */\r
     function binb(x, len) {\r
-      var j, i, \r
-          W = new Array(80);\r
+      var j, i, l,\r
+          W = new Array(80),\r
           hash = new Array(16),\r
           //Initial hash values\r
-          H = new Array(\r
+          H = [\r
             new int64(0x6a09e667, -205731576),\r
             new int64(-1150833019, -2067093701),\r
             new int64(0x3c6ef372, -23791573),\r
@@ -12334,7 +12332,7 @@ module.exports = ohauth;
             new int64(-1694144372, 0x2b3e6c1f),\r
             new int64(0x1f83d9ab, -79577749),\r
             new int64(0x5be0cd19, 0x137e2179)\r
-          ),\r
+          ],\r
           T1 = new int64(0, 0),\r
           T2 = new int64(0, 0),\r
           a = new int64(0,0),\r
@@ -12356,7 +12354,7 @@ module.exports = ohauth;
 \r
       if (sha512_k === undefined) {\r
           //SHA512 constants\r
-          sha512_k = new Array(\r
+          sha512_k = [\r
             new int64(0x428a2f98, -685199838), new int64(0x71374491, 0x23ef65cd),\r
             new int64(-1245643825, -330482897), new int64(-373957723, -2121671748),\r
             new int64(0x3956c25b, -213338824), new int64(0x59f111f1, -1241133031),\r
@@ -12397,7 +12395,7 @@ module.exports = ohauth;
             new int64(0x3c9ebe0a, 0x15c9bebc), new int64(0x431d67c4, -1676669620),\r
             new int64(0x4cc5d4be, -885112138), new int64(0x597f299c, -60457430),\r
             new int64(0x5fcb6fab, 0x3ad6faec), new int64(0x6c44198c, 0x4a475817)\r
-          );\r
+          ];\r
       }\r
   \r
       for (i=0; i<80; i+=1) {\r
@@ -12407,8 +12405,8 @@ module.exports = ohauth;
       // append padding to the source string. The format is described in the FIPS.\r
       x[len >> 5] |= 0x80 << (24 - (len & 0x1f));\r
       x[((len + 128 >> 10)<< 5) + 31] = len;\r
-    \r
-      for (i = 0; i<x.length; i+=32) { //32 dwords is the block size\r
+      l = x.length;\r
+      for (i = 0; i<l; i+=32) { //32 dwords is the block size\r
         int64copy(a, H[0]);\r
         int64copy(b, H[1]);\r
         int64copy(c, H[2]);\r
@@ -12418,12 +12416,12 @@ module.exports = ohauth;
         int64copy(g, H[6]);\r
         int64copy(h, H[7]);\r
       \r
-        for (j=0; j<16; j++) {\r
+        for (j=0; j<16; j+=1) {\r
           W[j].h = x[i + 2*j];\r
           W[j].l = x[i + 2*j + 1];\r
         }\r
       \r
-        for (j=16; j<80; j++) {\r
+        for (j=16; j<80; j+=1) {\r
           //sigma1\r
           int64rrot(r1, W[j-2], 19);\r
           int64revrrot(r2, W[j-2], 29);\r
@@ -12440,7 +12438,7 @@ module.exports = ohauth;
           int64add4(W[j], s1, W[j-7], s0, W[j-16]);\r
         }\r
       \r
-        for (j = 0; j < 80; j++) {\r
+        for (j = 0; j < 80; j+=1) {\r
           //Ch\r
           Ch.l = (e.l & f.l) ^ (~e.l & g.l);\r
           Ch.h = (e.h & f.h) ^ (~e.h & g.h);\r
@@ -12703,8 +12701,8 @@ module.exports = ohauth;
      * Convert an array of little-endian words to a string\r
      */\r
     function binl2rstr(input) {\r
-      var output = '', i = 0;\r
-      for (; i < input.length * 32; i += 8) {\r
+      var i, output = '', l = input.length * 32;\r
+      for (i = 0; i < l; i += 8) {\r
         output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF);\r
       }\r
       return output;\r
@@ -12714,21 +12712,23 @@ module.exports = ohauth;
      * Calculate the RIPE-MD160 of an array of little-endian words, and a bit length.\r
      */\r
     function binl(x, len) {\r
-      var T, j, i,\r
+      var T, j, i, l,\r
           h0 = 0x67452301,\r
           h1 = 0xefcdab89,\r
           h2 = 0x98badcfe,\r
           h3 = 0x10325476,\r
           h4 = 0xc3d2e1f0,\r
-          A1 = h0, B1 = h1, C1 = h2, D1 = h3, E1 = h4,\r
-          A2 = h0, B2 = h1, C2 = h2, D2 = h3, E2 = h4;\r
+          A1, B1, C1, D1, E1,\r
+          A2, B2, C2, D2, E2;\r
 \r
       /* append padding */\r
       x[len >> 5] |= 0x80 << (len % 32);\r
       x[(((len + 64) >>> 9) << 4) + 14] = len;\r
-\r
-      for (i = 0; i < x.length; i += 16) {\r
-        for (j = 0; j <= 79; ++j) {\r
+      l = x.length;\r
+      \r
+      for (i = 0; i < l; i+=16) {\r
+        A1 = A2 = h0; B1 = B2 = h1; C1 = C2 = h2; D1 = D2 = h3; E1 = E2 = h4;\r
+        for (j = 0; j <= 79; j+=1) {\r
           T = safe_add(A1, rmd160_f(j, B1, C1, D1));\r
           T = safe_add(T, x[i + rmd160_r1[j]]);\r
           T = safe_add(T, rmd160_K1(j));\r
@@ -12781,7 +12781,7 @@ module.exports = ohauth;
   }\r
 };\r
 \r
-  // expose Hashes Object\r
+  // exposes Hashes\r
   (function( window, undefined ) {\r
     var freeExports = false;\r
     if (typeof exports === 'object' ) {\r
@@ -14796,16 +14796,18 @@ window.iD = function () {
         mode,
         container,
         ui = iD.ui(context),
-        map = iD.Map(context);
-
-    // the connection requires .storage() to be available on calling.
-    var connection = iD.Connection(context)
-        .keys(iD.data.keys);
+        map = iD.Map(context),
+        connection = iD.Connection(context, iD.data.keys[0]);
 
     connection.on('load.context', function loadContext(err, result) {
         history.merge(result);
     });
 
+    context.preauth = function(options) {
+        connection.switch(options);
+        return context;
+    };
+
     /* Straight accessors. Avoid using these if you can. */
     context.ui = function() { return ui; };
     context.connection = function() { return connection; };
@@ -14974,21 +14976,19 @@ iD.detect = function() {
 
     return browser;
 };
-iD.Connection = function(context) {
+iD.Connection = function(context, options) {
 
     var event = d3.dispatch('auth', 'loading', 'load', 'loaded'),
-        url = 'http://www.openstreetmap.org',
+        url = options.url || 'http://www.openstreetmap.org',
         connection = {},
         user = {},
-        keys,
         inflight = {},
         loadedTiles = {},
         loadingModal,
-        oauth = osmAuth({
-            url: url,
+        oauth = osmAuth(_.extend({
             loading: authLoading,
             done: authDone
-        }),
+        }, options)),
         ndStr = 'nd',
         tagStr = 'tag',
         memberStr = 'member',
@@ -15001,6 +15001,14 @@ iD.Connection = function(context) {
         return url + '/browse/changeset/' + changesetId;
     };
 
+    connection.entityURL = function(entity) {
+        return url + '/browse/' + entity.type + '/' + entity.osmId();
+    };
+
+    connection.userUrl = function(username) {
+        return url + "/user/" + username;
+    };
+
     connection.loadFromURL = function(url, callback) {
         function done(dom) {
             return callback(null, parse(dom));
@@ -15219,12 +15227,13 @@ iD.Connection = function(context) {
     };
 
     connection.status = function(callback) {
-        function done(err, capabilities) {
-            if (err) return callback(err);
+        function done(capabilities) {
             var apiStatus = capabilities.getElementsByTagName('status');
             callback(undefined, apiStatus[0].getAttribute('api'));
         }
-        oauth.xhr({ method: 'GET', path: '/api/capabilities' }, done);
+        d3.xml(url + '/api/capabilities').get()
+            .on('load', done)
+            .on('error', callback);
     };
 
     function abortRequest(i) { i.abort(); }
@@ -15287,14 +15296,12 @@ iD.Connection = function(context) {
         });
     };
 
-    connection.userUrl = function(username) {
-        return url + "/user/" + username;
-    };
-
-    connection.url = function(_) {
-        if (!arguments.length) return url;
-        url = _;
-        oauth.url(_);
+    connection.switch = function(options) {
+        url = options.url;
+        oauth.options(_.extend({
+            loading: authLoading,
+            done: authDone
+        }, options));
         event.auth();
         connection.flush();
         return connection;
@@ -15330,13 +15337,6 @@ iD.Connection = function(context) {
         return connection;
     };
 
-    connection.keys = function(_) {
-        if (!arguments.length) return keys;
-        keys = _;
-        oauth.keys(keys);
-        return connection;
-    };
-
     connection.authenticate = function(callback) {
         function done(err, res) {
             event.auth();
@@ -17144,7 +17144,7 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) {
         return function(graph) {
             if (isArea) {
                 return graph
-                    .replace(way.removeNode(end.id).addNode(newNode.id, index))
+                    .replace(way.addNode(newNode.id, index))
                     .remove(end);
 
             } else {
@@ -22281,7 +22281,7 @@ iD.ui.Account = function(context) {
 
             // Link
             var userLink = selection.append('a')
-                .attr('href', connection.url() + '/user/' + details.display_name)
+                .attr('href', connection.userUrl(details.display_name))
                 .attr('target', '_blank');
 
             // Add thumbnail or dont
@@ -22522,15 +22522,22 @@ iD.ui.Background = function(context) {
         }
 
         function clickNudge(d) {
-            var interval = window.setInterval(nudge, 100);
+
+            var timeout = window.setTimeout(function() {
+                    interval = window.setInterval(nudge, 100);
+                }, 500),
+                interval;
 
             d3.select(this).on('mouseup', function() {
                 window.clearInterval(interval);
+                window.clearTimeout(timeout);
                 nudge();
             });
 
             function nudge() {
                 context.background().nudge(d[1], context.map().zoom());
+                var offset = context.background().offset();
+                resetButton.classed('disabled', offset[0] === 0 && offset[1] === 0);
                 context.redraw();
             }
         }
@@ -22684,9 +22691,10 @@ iD.ui.Background = function(context) {
             .on('mousedown', clickNudge);
 
         var resetButton = nudgeContainer.append('button')
-            .attr('class', 'reset')
+            .attr('class', 'reset disabled')
             .on('click', function () {
                 context.background().offset([0, 0]);
+                resetButton.classed('disabled', true);
                 context.redraw();
             });
 
@@ -22809,7 +22817,8 @@ iD.ui.Commit = function(context) {
         userLink.append('a')
             .attr('class','user-info')
             .text(user.display_name)
-            .attr('href', connection.url() + '/user/' + user.display_name)
+            .attr('href', connection.userUrl(user.display_name))
+            .attr('tabindex', -1)
             .attr('target', '_blank');
 
         saveSection.append('p')
@@ -24669,10 +24678,11 @@ iD.ui.SourceSwitch = function(context) {
         if (context.history().hasChanges() &&
             !window.confirm(t('source_switch.lose_changes'))) return;
 
-        var live = d3.select(this).classed('live');
+        var live = d3.select(this)
+            .classed('live');
 
         context.connection()
-            .url(live ? 'http://api06.dev.openstreetmap.org' : 'http://www.openstreetmap.org');
+            .switch(live ? iD.data.keys[1] : iD.data.keys[0]);
 
         context.map()
             .flush();
@@ -24837,7 +24847,8 @@ iD.ui.Success = function(connection) {
             .on('click.save', function() {
                 event.cancel();
             })
-            .append('span').attr('class','label').text('Okay');
+            .text('Okay')
+            .node().focus();
     }
 
     return d3.rebind(success, event, 'on');
@@ -24920,7 +24931,7 @@ iD.ui.TagEditor = function(context, entity) {
             var osmLink = tageditorpreset.append('div')
                 .attr('class', 'col12 inspector-inner')
                 .append('a')
-                .attr('href', 'http://www.openstreetmap.org/browse/' + entity.type + '/' + entity.osmId())
+                .attr('href', context.connection().entityURL(entity))
                 .attr('target', '_blank');
 
             osmLink.append('span')
@@ -26488,7 +26499,7 @@ iD.ui.intro.line = function(context, reveal) {
             timeout(function() {
                 d3.select('#curtain').style('pointer-events', 'none');
                 var road = d3.select('.preset-grid .grid-entry').filter(function(d) {
-                    return d.id === 'Road';
+                    return d.id === 'category-road';
                 });
                 reveal(road.node(), 'intro.lines.road');
                 road.one('click.intro', roadCategory);
@@ -54198,18 +54209,20 @@ iD.data = {
         "yh:TYPE",
         "yh:WIDTH_RANK"
     ],
-    "keys": {
-        "http://api06.dev.openstreetmap.org": {
-            "oauth_consumer_key": "zwQZFivccHkLs3a8Rq5CoS412fE5aPCXDw9DZj7R",
-            "oauth_secret": "aMnOOCwExO2XYtRVWJ1bI9QOdqh1cay2UgpbhA6p",
-            "oauth_signature_method": "HMAC-SHA1"
-        },
-        "http://www.openstreetmap.org": {
+    "keys": [
+        {
+            "url": "http://www.openstreetmap.org",
             "oauth_consumer_key": "5A043yRSEugj4DJ5TljuapfnrflWDte8jTOcWLlT",
             "oauth_secret": "aB3jKq1TRsCOUrfOIZ6oQMEDmv2ptV76PA54NGLL",
             "oauth_signature_method": "HMAC-SHA1"
+        },
+        {
+            "url": "http://api06.dev.openstreetmap.org",
+            "oauth_consumer_key": "zwQZFivccHkLs3a8Rq5CoS412fE5aPCXDw9DZj7R",
+            "oauth_secret": "aMnOOCwExO2XYtRVWJ1bI9QOdqh1cay2UgpbhA6p",
+            "oauth_signature_method": "HMAC-SHA1"
         }
-    },
+    ],
     "imagery": [
         {
             "name": "Bing aerial imagery",