]> git.openstreetmap.org Git - rails.git/blobdiff - vendor/assets/iD/iD.js
Merge remote-tracking branch 'upstream/pull/2947' into master
[rails.git] / vendor / assets / iD / iD.js
index 0d6cbd741e1fb322568386df743c750276136b71..4acb400030f075a0e55061fd09744c0fb806bb56 100644 (file)
@@ -32,7 +32,7 @@
          check(typeof self == 'object' && self) ||
          check(typeof commonjsGlobal == 'object' && commonjsGlobal) ||
          // eslint-disable-next-line no-new-func
-         Function('return this')();
+         (function () { return this; })() || Function('return this')();
 
        var fails = function (exec) {
          try {
        (module.exports = function (key, value) {
          return sharedStore[key] || (sharedStore[key] = value !== undefined ? value : {});
        })('versions', []).push({
-         version: '3.6.5',
+         version: '3.7.0',
          mode:  'global',
          copyright: '© 2020 Denis Pushkarev (zloirock.ru)'
        });
        };
 
        if (nativeWeakMap) {
-         var store$1 = new WeakMap$1();
+         var store$1 = sharedStore.state || (sharedStore.state = new WeakMap$1());
          var wmget = store$1.get;
          var wmhas = store$1.has;
          var wmset = store$1.set;
          set = function (it, metadata) {
+           metadata.facade = it;
            wmset.call(store$1, it, metadata);
            return metadata;
          };
          var STATE = sharedKey('state');
          hiddenKeys[STATE] = true;
          set = function (it, metadata) {
+           metadata.facade = it;
            createNonEnumerableProperty(it, STATE, metadata);
            return metadata;
          };
          var unsafe = options ? !!options.unsafe : false;
          var simple = options ? !!options.enumerable : false;
          var noTargetGet = options ? !!options.noTargetGet : false;
+         var state;
          if (typeof value == 'function') {
-           if (typeof key == 'string' && !has(value, 'name')) createNonEnumerableProperty(value, 'name', key);
-           enforceInternalState(value).source = TEMPLATE.join(typeof key == 'string' ? key : '');
+           if (typeof key == 'string' && !has(value, 'name')) {
+             createNonEnumerableProperty(value, 'name', key);
+           }
+           state = enforceInternalState(value);
+           if (!state.source) {
+             state.source = TEMPLATE.join(typeof key == 'string' ? key : '');
+           }
          }
          if (O === global_1) {
            if (simple) O[key] = value;
            || iterators[classof(it)];
        };
 
-       // call something on iterator step with safe closing on error
-       var callWithSafeIterationClosing = function (iterator, fn, value, ENTRIES) {
-         try {
-           return ENTRIES ? fn(anObject(value)[0], value[1]) : fn(value);
-         // 7.4.6 IteratorClose(iterator, completion)
-         } catch (error) {
-           var returnMethod = iterator['return'];
-           if (returnMethod !== undefined) anObject(returnMethod.call(iterator));
-           throw error;
+       var iteratorClose = function (iterator) {
+         var returnMethod = iterator['return'];
+         if (returnMethod !== undefined) {
+           return anObject(returnMethod.call(iterator)).value;
          }
        };
 
-       var iterate_1 = createCommonjsModule(function (module) {
        var Result = function (stopped, result) {
          this.stopped = stopped;
          this.result = result;
        };
 
-       var iterate = module.exports = function (iterable, fn, that, AS_ENTRIES, IS_ITERATOR) {
-         var boundFunction = functionBindContext(fn, that, AS_ENTRIES ? 2 : 1);
+       var iterate = function (iterable, unboundFunction, options) {
+         var that = options && options.that;
+         var AS_ENTRIES = !!(options && options.AS_ENTRIES);
+         var IS_ITERATOR = !!(options && options.IS_ITERATOR);
+         var INTERRUPTED = !!(options && options.INTERRUPTED);
+         var fn = functionBindContext(unboundFunction, that, 1 + AS_ENTRIES + INTERRUPTED);
          var iterator, iterFn, index, length, result, next, step;
 
+         var stop = function (condition) {
+           if (iterator) iteratorClose(iterator);
+           return new Result(true, condition);
+         };
+
+         var callFn = function (value) {
+           if (AS_ENTRIES) {
+             anObject(value);
+             return INTERRUPTED ? fn(value[0], value[1], stop) : fn(value[0], value[1]);
+           } return INTERRUPTED ? fn(value, stop) : fn(value);
+         };
+
          if (IS_ITERATOR) {
            iterator = iterable;
          } else {
            // optimisation for array iterators
            if (isArrayIteratorMethod(iterFn)) {
              for (index = 0, length = toLength(iterable.length); length > index; index++) {
-               result = AS_ENTRIES
-                 ? boundFunction(anObject(step = iterable[index])[0], step[1])
-                 : boundFunction(iterable[index]);
+               result = callFn(iterable[index]);
                if (result && result instanceof Result) return result;
              } return new Result(false);
            }
 
          next = iterator.next;
          while (!(step = next.call(iterator)).done) {
-           result = callWithSafeIterationClosing(iterator, boundFunction, step.value, AS_ENTRIES);
+           try {
+             result = callFn(step.value);
+           } catch (error) {
+             iteratorClose(iterator);
+             throw error;
+           }
            if (typeof result == 'object' && result && result instanceof Result) return result;
          } return new Result(false);
        };
 
-       iterate.stop = function (result) {
-         return new Result(true, result);
-       };
-       });
-
        var ITERATOR$4 = wellKnownSymbol('iterator');
        var SAFE_CLOSING = false;
 
 
        var engineIsIos = /(iphone|ipod|ipad).*applewebkit/i.test(engineUserAgent);
 
+       var engineIsNode = classofRaw(global_1.process) == 'process';
+
        var location$1 = global_1.location;
        var set$2 = global_1.setImmediate;
        var clear = global_1.clearImmediate;
            delete queue[id];
          };
          // Node.js 0.8-
-         if (classofRaw(process$2) == 'process') {
+         if (engineIsNode) {
            defer = function (id) {
              process$2.nextTick(runner(id));
            };
            global_1.addEventListener &&
            typeof postMessage == 'function' &&
            !global_1.importScripts &&
-           !fails(post) &&
-           location$1.protocol !== 'file:'
+           location$1 && location$1.protocol !== 'file:' &&
+           !fails(post)
          ) {
            defer = post;
            global_1.addEventListener('message', listener, false);
        };
 
        var getOwnPropertyDescriptor$2 = objectGetOwnPropertyDescriptor.f;
-
        var macrotask = task.set;
 
 
+
        var MutationObserver = global_1.MutationObserver || global_1.WebKitMutationObserver;
+       var document$2 = global_1.document;
        var process$3 = global_1.process;
        var Promise$1 = global_1.Promise;
-       var IS_NODE = classofRaw(process$3) == 'process';
        // Node.js 11 shows ExperimentalWarning on getting `queueMicrotask`
        var queueMicrotaskDescriptor = getOwnPropertyDescriptor$2(global_1, 'queueMicrotask');
        var queueMicrotask = queueMicrotaskDescriptor && queueMicrotaskDescriptor.value;
        if (!queueMicrotask) {
          flush = function () {
            var parent, fn;
-           if (IS_NODE && (parent = process$3.domain)) parent.exit();
+           if (engineIsNode && (parent = process$3.domain)) parent.exit();
            while (head) {
              fn = head.fn;
              head = head.next;
            if (parent) parent.enter();
          };
 
-         // Node.js
-         if (IS_NODE) {
-           notify = function () {
-             process$3.nextTick(flush);
-           };
          // browsers with MutationObserver, except iOS - https://github.com/zloirock/core-js/issues/339
-         } else if (MutationObserver && !engineIsIos) {
+         if (!engineIsIos && !engineIsNode && MutationObserver && document$2) {
            toggle = true;
-           node = document.createTextNode('');
+           node = document$2.createTextNode('');
            new MutationObserver(flush).observe(node, { characterData: true });
            notify = function () {
              node.data = toggle = !toggle;
            notify = function () {
              then.call(promise, flush);
            };
+         // Node.js without promises
+         } else if (engineIsNode) {
+           notify = function () {
+             process$3.nextTick(flush);
+           };
          // for other environments - macrotask based on:
          // - setImmediate
          // - MessageChannel
 
 
 
+
        var SPECIES$5 = wellKnownSymbol('species');
        var PROMISE = 'Promise';
        var getInternalState$3 = internalState.get;
        var getInternalPromiseState = internalState.getterFor(PROMISE);
        var PromiseConstructor = nativePromiseConstructor;
        var TypeError$1 = global_1.TypeError;
-       var document$2 = global_1.document;
+       var document$3 = global_1.document;
        var process$4 = global_1.process;
        var $fetch = getBuiltIn('fetch');
        var newPromiseCapability$1 = newPromiseCapability.f;
        var newGenericPromiseCapability = newPromiseCapability$1;
-       var IS_NODE$1 = classofRaw(process$4) == 'process';
-       var DISPATCH_EVENT = !!(document$2 && document$2.createEvent && global_1.dispatchEvent);
+       var DISPATCH_EVENT = !!(document$3 && document$3.createEvent && global_1.dispatchEvent);
+       var NATIVE_REJECTION_EVENT = typeof PromiseRejectionEvent == 'function';
        var UNHANDLED_REJECTION = 'unhandledrejection';
        var REJECTION_HANDLED = 'rejectionhandled';
        var PENDING = 0;
            // We can't detect it synchronously, so just check versions
            if (engineV8Version === 66) return true;
            // Unhandled rejections tracking support, NodeJS Promise without it fails @@species test
-           if (!IS_NODE$1 && typeof PromiseRejectionEvent != 'function') return true;
+           if (!engineIsNode && !NATIVE_REJECTION_EVENT) return true;
          }
          // We can't use @@species feature detection in V8 since it causes
          // deoptimization and performance degradation
          return isObject(it) && typeof (then = it.then) == 'function' ? then : false;
        };
 
-       var notify$1 = function (promise, state, isReject) {
+       var notify$1 = function (state, isReject) {
          if (state.notified) return;
          state.notified = true;
          var chain = state.reactions;
              try {
                if (handler) {
                  if (!ok) {
-                   if (state.rejection === UNHANDLED) onHandleUnhandled(promise, state);
+                   if (state.rejection === UNHANDLED) onHandleUnhandled(state);
                    state.rejection = HANDLED;
                  }
                  if (handler === true) result = value;
            }
            state.reactions = [];
            state.notified = false;
-           if (isReject && !state.rejection) onUnhandled(promise, state);
+           if (isReject && !state.rejection) onUnhandled(state);
          });
        };
 
        var dispatchEvent = function (name, promise, reason) {
          var event, handler;
          if (DISPATCH_EVENT) {
-           event = document$2.createEvent('Event');
+           event = document$3.createEvent('Event');
            event.promise = promise;
            event.reason = reason;
            event.initEvent(name, false, true);
            global_1.dispatchEvent(event);
          } else event = { promise: promise, reason: reason };
-         if (handler = global_1['on' + name]) handler(event);
+         if (!NATIVE_REJECTION_EVENT && (handler = global_1['on' + name])) handler(event);
          else if (name === UNHANDLED_REJECTION) hostReportErrors('Unhandled promise rejection', reason);
        };
 
-       var onUnhandled = function (promise, state) {
+       var onUnhandled = function (state) {
          task$1.call(global_1, function () {
+           var promise = state.facade;
            var value = state.value;
            var IS_UNHANDLED = isUnhandled(state);
            var result;
            if (IS_UNHANDLED) {
              result = perform(function () {
-               if (IS_NODE$1) {
+               if (engineIsNode) {
                  process$4.emit('unhandledRejection', value, promise);
                } else dispatchEvent(UNHANDLED_REJECTION, promise, value);
              });
              // Browsers should not trigger `rejectionHandled` event if it was handled here, NodeJS - should
-             state.rejection = IS_NODE$1 || isUnhandled(state) ? UNHANDLED : HANDLED;
+             state.rejection = engineIsNode || isUnhandled(state) ? UNHANDLED : HANDLED;
              if (result.error) throw result.value;
            }
          });
          return state.rejection !== HANDLED && !state.parent;
        };
 
-       var onHandleUnhandled = function (promise, state) {
+       var onHandleUnhandled = function (state) {
          task$1.call(global_1, function () {
-           if (IS_NODE$1) {
+           var promise = state.facade;
+           if (engineIsNode) {
              process$4.emit('rejectionHandled', promise);
            } else dispatchEvent(REJECTION_HANDLED, promise, state.value);
          });
        };
 
-       var bind = function (fn, promise, state, unwrap) {
+       var bind = function (fn, state, unwrap) {
          return function (value) {
-           fn(promise, state, value, unwrap);
+           fn(state, value, unwrap);
          };
        };
 
-       var internalReject = function (promise, state, value, unwrap) {
+       var internalReject = function (state, value, unwrap) {
          if (state.done) return;
          state.done = true;
          if (unwrap) state = unwrap;
          state.value = value;
          state.state = REJECTED;
-         notify$1(promise, state, true);
+         notify$1(state, true);
        };
 
-       var internalResolve = function (promise, state, value, unwrap) {
+       var internalResolve = function (state, value, unwrap) {
          if (state.done) return;
          state.done = true;
          if (unwrap) state = unwrap;
          try {
-           if (promise === value) throw TypeError$1("Promise can't be resolved itself");
+           if (state.facade === value) throw TypeError$1("Promise can't be resolved itself");
            var then = isThenable(value);
            if (then) {
              microtask(function () {
                var wrapper = { done: false };
                try {
                  then.call(value,
-                   bind(internalResolve, promise, wrapper, state),
-                   bind(internalReject, promise, wrapper, state)
+                   bind(internalResolve, wrapper, state),
+                   bind(internalReject, wrapper, state)
                  );
                } catch (error) {
-                 internalReject(promise, wrapper, error, state);
+                 internalReject(wrapper, error, state);
                }
              });
            } else {
              state.value = value;
              state.state = FULFILLED;
-             notify$1(promise, state, false);
+             notify$1(state, false);
            }
          } catch (error) {
-           internalReject(promise, { done: false }, error, state);
+           internalReject({ done: false }, error, state);
          }
        };
 
            Internal.call(this);
            var state = getInternalState$3(this);
            try {
-             executor(bind(internalResolve, this, state), bind(internalReject, this, state));
+             executor(bind(internalResolve, state), bind(internalReject, state));
            } catch (error) {
-             internalReject(this, state, error);
+             internalReject(state, error);
            }
          };
          // eslint-disable-next-line no-unused-vars
              var reaction = newPromiseCapability$1(speciesConstructor(this, PromiseConstructor));
              reaction.ok = typeof onFulfilled == 'function' ? onFulfilled : true;
              reaction.fail = typeof onRejected == 'function' && onRejected;
-             reaction.domain = IS_NODE$1 ? process$4.domain : undefined;
+             reaction.domain = engineIsNode ? process$4.domain : undefined;
              state.parent = true;
              state.reactions.push(reaction);
-             if (state.state != PENDING) notify$1(this, state, false);
+             if (state.state != PENDING) notify$1(state, false);
              return reaction.promise;
            },
            // `Promise.prototype.catch` method
            var promise = new Internal();
            var state = getInternalState$3(promise);
            this.promise = promise;
-           this.resolve = bind(internalResolve, promise, state);
-           this.reject = bind(internalReject, promise, state);
+           this.resolve = bind(internalResolve, state);
+           this.reject = bind(internalReject, state);
          };
          newPromiseCapability.f = newPromiseCapability$1 = function (C) {
            return C === PromiseConstructor || C === PromiseWrapper
              var values = [];
              var counter = 0;
              var remaining = 1;
-             iterate_1(iterable, function (promise) {
+             iterate(iterable, function (promise) {
                var index = counter++;
                var alreadyCalled = false;
                values.push(undefined);
            var reject = capability.reject;
            var result = perform(function () {
              var $promiseResolve = aFunction$1(C.resolve);
-             iterate_1(iterable, function (promise) {
+             iterate(iterable, function (promise) {
                $promiseResolve.call(C, promise).then(capability.resolve, reject);
              });
            });
          } return T;
        } : nativeAssign;
 
+       // call something on iterator step with safe closing on error
+       var callWithSafeIterationClosing = function (iterator, fn, value, ENTRIES) {
+         try {
+           return ENTRIES ? fn(anObject(value)[0], value[1]) : fn(value);
+         // 7.4.6 IteratorClose(iterator, completion)
+         } catch (error) {
+           iteratorClose(iterator);
+           throw error;
+         }
+       };
+
        // `Array.from` method implementation
        // https://tc39.github.io/ecma262/#sec-array.from
        var arrayFrom = function from(arrayLike /* , mapfn = undefined, thisArg = undefined */) {
        var URLSearchParamsPrototype = URLSearchParamsConstructor.prototype;
 
        redefineAll(URLSearchParamsPrototype, {
-         // `URLSearchParams.prototype.appent` method
+         // `URLSearchParams.prototype.append` method
          // https://url.spec.whatwg.org/#dom-urlsearchparams-append
          append: function append(name, value) {
            validateArgumentsLength(arguments.length, 2);
          var headers = new Headers$1(); // Replace instances of \r\n and \n followed by at least one space or horizontal tab with a space
          // https://tools.ietf.org/html/rfc7230#section-3.2
 
-         var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, ' ');
-         preProcessedHeaders.split(/\r?\n/).forEach(function (line) {
+         var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, ' '); // Avoiding split via regex to work around a common IE11 bug with the core-js 3.6.0 regex polyfill
+         // https://github.com/github/fetch/issues/748
+         // https://github.com/zloirock/core-js/issues/751
+
+         preProcessedHeaders.split('\r').map(function (header) {
+           return header.indexOf('\n') === 0 ? header.substr(1, header.length) : header;
+         }).forEach(function (line) {
            var parts = line.split(':');
            var key = parts.shift().trim();
 
              Constructor = wrapper(function (dummy, iterable) {
                anInstance(dummy, Constructor, CONSTRUCTOR_NAME);
                var that = inheritIfRequired(new NativeConstructor(), dummy, Constructor);
-               if (iterable != undefined) iterate_1(iterable, that[ADDER], that, IS_MAP);
+               if (iterable != undefined) iterate(iterable, that[ADDER], { that: that, AS_ENTRIES: IS_MAP });
                return that;
              });
              Constructor.prototype = NativePrototype;
                size: 0
              });
              if (!descriptors) that.size = 0;
-             if (iterable != undefined) iterate_1(iterable, that[ADDER], that, IS_MAP);
+             if (iterable != undefined) iterate(iterable, that[ADDER], { that: that, AS_ENTRIES: IS_MAP });
            });
 
            var getInternalState = internalStateGetterFor(CONSTRUCTOR_NAME);
 
 
 
+
+
        var STRICT_METHOD$7 = arrayMethodIsStrict('reduce');
        var USES_TO_LENGTH$9 = arrayMethodUsesToLength('reduce', { 1: 0 });
+       // Chrome 80-82 has a critical bug
+       // https://bugs.chromium.org/p/chromium/issues/detail?id=1049982
+       var CHROME_BUG = !engineIsNode && engineV8Version > 79 && engineV8Version < 83;
 
        // `Array.prototype.reduce` method
        // https://tc39.github.io/ecma262/#sec-array.prototype.reduce
-       _export({ target: 'Array', proto: true, forced: !STRICT_METHOD$7 || !USES_TO_LENGTH$9 }, {
+       _export({ target: 'Array', proto: true, forced: !STRICT_METHOD$7 || !USES_TO_LENGTH$9 || CHROME_BUG }, {
          reduce: function reduce(callbackfn /* , initialValue */) {
            return $reduce$1(this, callbackfn, arguments.length, arguments.length > 1 ? arguments[1] : undefined);
          }
          var regexp = /./;
          try {
            '/./'[METHOD_NAME](regexp);
-         } catch (e) {
+         } catch (error1) {
            try {
              regexp[MATCH$2] = false;
              return '/./'[METHOD_NAME](regexp);
-           } catch (f) { /* empty */ }
+           } catch (error2) { /* empty */ }
          } return false;
        };
 
          return action;
        }
 
+       var geojsonRewind = rewind;
+
+       function rewind(gj, outer) {
+         var type = gj && gj.type,
+             i;
+
+         if (type === 'FeatureCollection') {
+           for (i = 0; i < gj.features.length; i++) {
+             rewind(gj.features[i], outer);
+           }
+         } else if (type === 'GeometryCollection') {
+           for (i = 0; i < gj.geometries.length; i++) {
+             rewind(gj.geometries[i], outer);
+           }
+         } else if (type === 'Feature') {
+           rewind(gj.geometry, outer);
+         } else if (type === 'Polygon') {
+           rewindRings(gj.coordinates, outer);
+         } else if (type === 'MultiPolygon') {
+           for (i = 0; i < gj.coordinates.length; i++) {
+             rewindRings(gj.coordinates[i], outer);
+           }
+         }
+
+         return gj;
+       }
+
+       function rewindRings(rings, outer) {
+         if (rings.length === 0) return;
+         rewindRing(rings[0], outer);
+
+         for (var i = 1; i < rings.length; i++) {
+           rewindRing(rings[i], !outer);
+         }
+       }
+
+       function rewindRing(ring, dir) {
+         var area = 0;
+
+         for (var i = 0, len = ring.length, j = len - 1; i < len; j = i++) {
+           area += (ring[i][0] - ring[j][0]) * (ring[j][1] + ring[i][1]);
+         }
+
+         if (area >= 0 !== !!dir) ring.reverse();
+       }
+
        function actionExtract(entityID) {
          var extractedNodeID;
 
            var fromGeometry = entity.geometry(graph);
            var keysToCopyAndRetain = ['source', 'wheelchair'];
            var keysToRetain = ['area'];
-           var buildingKeysToRetain = ['architect', 'building', 'height', 'layer'];
-           var extractedLoc = d3_geoCentroid(entity.asGeoJSON(graph));
+           var buildingKeysToRetain = ['architect', 'building', 'height', 'layer']; // d3_geoCentroid is wrong for counterclockwise-wound polygons, so wind them clockwise
+
+           var extractedLoc = d3_geoCentroid(geojsonRewind(Object.assign({}, entity.asGeoJSON(graph)), true));
 
            if (!extractedLoc || !isFinite(extractedLoc[0]) || !isFinite(extractedLoc[1])) {
              extractedLoc = entity.extent(graph).center();
                var raw = cap[0];
                var bull = cap[2];
                var isordered = bull.length > 1;
-               var isparen = bull[bull.length - 1] === ')';
                var list = {
                  type: 'list',
                  raw: raw,
                var next = false,
                    item,
                    space,
-                   b,
+                   bcurr,
+                   bnext,
                    addBack,
                    loose,
                    istask,
                    ischecked;
                var l = itemMatch.length;
+               bcurr = this.rules.block.listItemStart.exec(itemMatch[0]);
 
                for (var i = 0; i < l; i++) {
                  item = itemMatch[i];
-                 raw = item; // Remove the list item's bullet
+                 raw = item; // Determine whether the next list item belongs here.
+                 // Backpedal if it does not belong in this list.
+
+                 if (i !== l - 1) {
+                   bnext = this.rules.block.listItemStart.exec(itemMatch[i + 1]);
+
+                   if (bnext[1].length > bcurr[0].length || bnext[1].length > 3) {
+                     // nested list
+                     itemMatch.splice(i, 2, itemMatch[i] + '\n' + itemMatch[i + 1]);
+                     i--;
+                     l--;
+                     continue;
+                   } else {
+                     if ( // different bullet style
+                     !this.options.pedantic || this.options.smartLists ? bnext[2][bnext[2].length - 1] !== bull[bull.length - 1] : isordered === (bnext[2].length === 1)) {
+                       addBack = itemMatch.slice(i + 1).join('\n');
+                       list.raw = list.raw.substring(0, list.raw.length - addBack.length);
+                       i = l - 1;
+                     }
+                   }
+
+                   bcurr = bnext;
+                 } // Remove the list item's bullet
                  // so it is seen as the next token.
 
+
                  space = item.length;
                  item = item.replace(/^ *([*+-]|\d+[.)]) ?/, ''); // Outdent whatever the
                  // list item contains. Hacky.
                  if (~item.indexOf('\n ')) {
                    space -= item.length;
                    item = !this.options.pedantic ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '') : item.replace(/^ {1,4}/gm, '');
-                 } // Determine whether the next list item belongs here.
-                 // Backpedal if it does not belong in this list.
-
-
-                 if (i !== l - 1) {
-                   b = this.rules.block.bullet.exec(itemMatch[i + 1])[0];
-
-                   if (isordered ? b.length === 1 || !isparen && b[b.length - 1] === ')' : b.length > 1 || this.options.smartLists && b !== bull) {
-                     addBack = itemMatch.slice(i + 1).join('\n');
-                     list.raw = list.raw.substring(0, list.raw.length - addBack.length);
-                     i = l - 1;
-                   }
                  } // Determine whether item is loose or not.
                  // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
                  // for discount behavior.
          hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,
          heading: /^ {0,3}(#{1,6}) +([^\n]*?)(?: +#+)? *(?:\n+|$)/,
          blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,
-         list: /^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
+         list: /^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?! {0,3}bull )\n*|\s*$)/,
          html: '^ {0,3}(?:' // optional indentation
          + '<(script|pre|style)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)' // (1)
          + '|comment[^\\n]*(\\n+|$)' // (2)
        block._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/;
        block.def = edit$1(block.def).replace('label', block._label).replace('title', block._title).getRegex();
        block.bullet = /(?:[*+-]|\d{1,9}[.)])/;
-       block.item = /^( *)(bull) ?[^\n]*(?:\n(?!\1bull ?)[^\n]*)*/;
+       block.item = /^( *)(bull) ?[^\n]*(?:\n(?! *bull ?)[^\n]*)*/;
        block.item = edit$1(block.item, 'gm').replace(/bull/g, block.bullet).getRegex();
+       block.listItemStart = edit$1(/^( *)(bull)/).replace('bull', block.bullet).getRegex();
        block.list = edit$1(block.list).replace(/bull/g, block.bullet).replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))').replace('def', '\\n+(?=' + block.def.source + ')').getRegex();
        block._tag = 'address|article|aside|base|basefont|blockquote|body|caption' + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption' + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe' + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option' + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr' + '|track|ul';
        block._comment = /<!--(?!-?>)[\s\S]*?(?:-->|$)/;
                  loc: loc,
                  key: feature.properties.key,
                  value: feature.properties.value,
-                 "package": feature.properties["package"],
-                 detections: feature.properties.detections
+                 detections: feature.properties.detections,
+                 direction: feature.properties.direction,
+                 accuracy: feature.properties.accuracy,
+                 first_seen_at: feature.properties.first_seen_at,
+                 last_seen_at: feature.properties.last_seen_at
                };
              }
 
                  key: feature.properties.key,
                  image_key: feature.properties.image_key,
                  value: feature.properties.value,
-                 "package": feature.properties["package"],
                  shape: feature.properties.shape
                };
 
          return _boolean(subject, clipping, UNION);
        }
 
+       /*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
        var read$6 = function read(buffer, offset, isLE, mLen, nBytes) {
          var e, m;
          var eLen = nBytes * 8 - mLen - 1;
                    if (buildingKeysToKeep.indexOf(key) !== -1 || key.match(/^building:.{1,}/) || key.match(/^roof:.{1,}/)) continue;
                  }
 
-                 if (type !== 'generic' && key.match(/^addr:.{1,}/)) continue;
+                 if (type !== 'generic') {
+                   if (key.match(/^addr:.{1,}/) || key.match(/^source:.{1,}/)) continue;
+                 }
+
                  delete tags[key];
                }
 
            layer.selectAll('.viewfield-group.currentView').attr('transform', transform);
          }
 
-         context.photos().on('change.streetside', update);
-
          function filterBubbles(bubbles) {
            var fromDate = context.photos().fromDate();
            var toDate = context.photos().toDate();
 
            if (svgStreetside.enabled) {
              showLayer();
+             context.photos().on('change.streetside', update);
            } else {
              hideLayer();
+             context.photos().on('change.streetside', null);
            }
 
            dispatch.call('change');
            return t;
          }
 
-         context.photos().on('change.mapillary_images', update);
-
          function filterImages(images) {
            var showsPano = context.photos().showsPanoramic();
            var showsFlat = context.photos().showsFlat();
 
            if (svgMapillaryImages.enabled) {
              showLayer();
+             context.photos().on('change.mapillary_images', update);
            } else {
              hideLayer();
+             context.photos().on('change.mapillary_images', null);
            }
 
            dispatch.call('change');
            }
          }
 
+         function filterData(detectedFeatures) {
+           var service = getService();
+           var fromDate = context.photos().fromDate();
+           var toDate = context.photos().toDate();
+           var usernames = context.photos().usernames();
+
+           if (fromDate) {
+             var fromTimestamp = new Date(fromDate).getTime();
+             detectedFeatures = detectedFeatures.filter(function (feature) {
+               return new Date(feature.last_seen_at).getTime() >= fromTimestamp;
+             });
+           }
+
+           if (toDate) {
+             var toTimestamp = new Date(toDate).getTime();
+             detectedFeatures = detectedFeatures.filter(function (feature) {
+               return new Date(feature.first_seen_at).getTime() <= toTimestamp;
+             });
+           }
+
+           if (usernames && service) {
+             detectedFeatures = detectedFeatures.filter(function (feature) {
+               return feature.detections.some(function (detection) {
+                 var imageKey = detection.image_key;
+                 var image = service.cachedImage(imageKey);
+                 return image && usernames.indexOf(image.captured_by) !== -1;
+               });
+             });
+           }
+
+           return detectedFeatures;
+         }
+
          function update() {
            var service = getService();
            var data = service ? service.signs(projection) : [];
+           data = filterData(data);
            var selectedImageKey = service.getSelectedImageKey();
            var transform = svgPointTransform(projection);
            var signs = layer.selectAll('.icon-sign').data(data, function (d) {
 
            if (svgMapillarySigns.enabled) {
              showLayer();
+             context.photos().on('change.mapillary_signs', update);
            } else {
              hideLayer();
+             context.photos().on('change.mapillary_signs', null);
            }
 
            dispatch.call('change');
            }
          }
 
+         function filterData(detectedFeatures) {
+           var service = getService();
+           var fromDate = context.photos().fromDate();
+           var toDate = context.photos().toDate();
+           var usernames = context.photos().usernames();
+
+           if (fromDate) {
+             var fromTimestamp = new Date(fromDate).getTime();
+             detectedFeatures = detectedFeatures.filter(function (feature) {
+               return new Date(feature.last_seen_at).getTime() >= fromTimestamp;
+             });
+           }
+
+           if (toDate) {
+             var toTimestamp = new Date(toDate).getTime();
+             detectedFeatures = detectedFeatures.filter(function (feature) {
+               return new Date(feature.first_seen_at).getTime() <= toTimestamp;
+             });
+           }
+
+           if (usernames && service) {
+             detectedFeatures = detectedFeatures.filter(function (feature) {
+               return feature.detections.some(function (detection) {
+                 var imageKey = detection.image_key;
+                 var image = service.cachedImage(imageKey);
+                 return image && usernames.indexOf(image.captured_by) !== -1;
+               });
+             });
+           }
+
+           return detectedFeatures;
+         }
+
          function update() {
            var service = getService();
            var data = service ? service.mapFeatures(projection) : [];
+           data = filterData(data);
            var selectedImageKey = service && service.getSelectedImageKey();
            var transform = svgPointTransform(projection);
            var mapFeatures = layer.selectAll('.icon-map-feature').data(data, function (d) {
 
            if (svgMapillaryMapFeatures.enabled) {
              showLayer();
+             context.photos().on('change.mapillary_map_features', update);
            } else {
              hideLayer();
+             context.photos().on('change.mapillary_map_features', null);
            }
 
            dispatch.call('change');
            return t;
          }
 
-         context.photos().on('change.openstreetcam_images', update);
-
          function filterImages(images) {
            var fromDate = context.photos().fromDate();
            var toDate = context.photos().toDate();
 
            if (svgOpenstreetcamImages.enabled) {
              showLayer();
+             context.photos().on('change.openstreetcam_images', update);
            } else {
              hideLayer();
+             context.photos().on('change.openstreetcam_images', null);
            }
 
            dispatch.call('change');
            return result;
          }
 
+         var _isImperial = !_mainLocalizer.usesMetric();
+
          function redraw(selection) {
            var graph = context.graph();
            var selectedNoteID = context.selectedNoteID();
            var osm = services.osm;
-           var isImperial = !_mainLocalizer.usesMetric();
            var localeCode = _mainLocalizer.localeCode();
            var heading;
            var center, location, centroid;
                  if (geometry === 'line' || geometry === 'area') {
                    closed = entity.type === 'relation' || entity.isClosed() && !entity.isDegenerate();
                    var feature = entity.asGeoJSON(graph);
-                   length += radiansToMeters(d3_geoLength(toLineString(feature)));
-                   centroid = d3_geoCentroid(feature);
+                   length += radiansToMeters(d3_geoLength(toLineString(feature))); // d3_geoCentroid is wrong for counterclockwise-wound polygons, so wind them clockwise
+
+                   centroid = d3_geoCentroid(geojsonRewind(Object.assign({}, feature), true));
 
                    if (closed) {
                      area += steradiansToSqmeters(entity.area(graph));
            }
 
            if (area) {
-             list.append('li').html(_t.html('info_panels.measurement.area') + ':').append('span').html(displayArea(area, isImperial));
+             list.append('li').html(_t.html('info_panels.measurement.area') + ':').append('span').html(displayArea(area, _isImperial));
            }
 
            if (length) {
-             list.append('li').html(_t.html('info_panels.measurement.' + (closed ? 'perimeter' : 'length')) + ':').append('span').html(displayLength(length, isImperial));
+             list.append('li').html(_t.html('info_panels.measurement.' + (closed ? 'perimeter' : 'length')) + ':').append('span').html(displayLength(length, _isImperial));
            }
 
            if (typeof distance === 'number') {
-             list.append('li').html(_t.html('info_panels.measurement.distance') + ':').append('span').html(displayLength(distance, isImperial));
+             list.append('li').html(_t.html('info_panels.measurement.distance') + ':').append('span').html(displayLength(distance, _isImperial));
            }
 
            if (location) {
            }
 
            if (length || area || typeof distance === 'number') {
-             var toggle = isImperial ? 'imperial' : 'metric';
+             var toggle = _isImperial ? 'imperial' : 'metric';
              selection.append('a').html(_t.html('info_panels.measurement.' + toggle)).attr('href', '#').attr('class', 'button button-toggle-units').on('click', function (d3_event) {
                d3_event.preventDefault();
-               isImperial = !isImperial;
+               _isImperial = !_isImperial;
                selection.call(redraw);
              });
            }
            context.history().on('change.intro', function (changed) {
              wasChanged = true;
              timeout(function () {
-               if (context.history().undoAnnotation() === _t('operations.split.annotation.line')) {
+               if (context.history().undoAnnotation() === _t('operations.split.annotation.line', {
+                 n: 1
+               })) {
                  _washingtonSegmentID = changed.created()[0].id;
                  continueTo(didSplit);
                } else {
 
          var _selection = select(null);
 
+         var _dataShortcuts;
+
          function shortcutsModal(_modalSelection) {
            _modalSelection.select('.modal').classed('modal-shortcuts', true);
 
 
            content.append('div').attr('class', 'modal-section').append('h3').html(_t.html('shortcuts.title'));
            _mainFileFetcher.get('shortcuts').then(function (data) {
-             content.call(render, data);
+             _dataShortcuts = data;
+             content.call(render);
            })["catch"](function () {
              /* ignore */
            });
          }
 
-         function render(selection, dataShortcuts) {
+         function render(selection) {
+           if (!_dataShortcuts) return;
            var wrapper = selection.selectAll('.wrapper').data([0]);
            var wrapperEnter = wrapper.enter().append('div').attr('class', 'wrapper modal-section');
            var tabsBar = wrapperEnter.append('div').attr('class', 'tabs-bar');
            var shortcutsList = wrapperEnter.append('div').attr('class', 'shortcuts-list');
            wrapper = wrapper.merge(wrapperEnter);
-           var tabs = tabsBar.selectAll('.tab').data(dataShortcuts);
-           var tabsEnter = tabs.enter().append('a').attr('class', 'tab').attr('href', '#').on('click', function (d3_event) {
+           var tabs = tabsBar.selectAll('.tab').data(_dataShortcuts);
+           var tabsEnter = tabs.enter().append('a').attr('class', 'tab').attr('href', '#').on('click', function (d3_event, d) {
              d3_event.preventDefault();
-             var i = tabs.nodes().indexOf(this);
+
+             var i = _dataShortcuts.indexOf(d);
+
              _activeTab = i;
-             render(selection, dataShortcuts);
+             render(selection);
            });
            tabsEnter.append('span').html(function (d) {
              return _t.html(d.text);
            wrapper.selectAll('.tab').classed('active', function (d, i) {
              return i === _activeTab;
            });
-           var shortcuts = shortcutsList.selectAll('.shortcut-tab').data(dataShortcuts);
+           var shortcuts = shortcutsList.selectAll('.shortcut-tab').data(_dataShortcuts);
            var shortcutsEnter = shortcuts.enter().append('div').attr('class', function (d) {
              return 'shortcut-tab shortcut-tab-' + d.tab;
            });
              if (_isMulti) {
                // Build _multiData array containing keys already set..
                for (var k in tags) {
-                 if (field.key && k.indexOf(field.key) !== 0 || field.keys.indexOf(k) === -1) continue;
+                 if (field.key && k.indexOf(field.key) !== 0) continue;
+                 if (!field.key && field.keys.indexOf(k) === -1) continue;
                  var v = tags[k];
                  if (!v || typeof v === 'string' && v.toLowerCase() === 'no') continue;
-                 var suffix = field.key ? k.substring(field.key.length) : k;
+                 var suffix = field.key ? k.substr(field.key.length) : k;
 
                  _multiData.push({
                    key: k,
            var existingLangs = new Set(existingLangsOrdered.filter(Boolean));
 
            for (var k in tags) {
-             var m = k.match(/^(.*):([a-zA-Z_-]+)$/);
+             var m = k.match(/^(.*):(.+)$/);
 
              if (m && m[1] === field.key && m[2]) {
                var item = {
              return d.lang && d.value;
            });
            utilGetSetValue(entries.select('.localized-lang'), function (d) {
-             return _mainLocalizer.languageName(d.lang);
+             var langItem = _languagesArray.find(function (item) {
+               return item.code === d.lang;
+             });
+
+             if (langItem) return langItem.label;
+             return d.lang;
            });
            utilGetSetValue(entries.select('.localized-value'), function (d) {
              return typeof d.value === 'string' ? d.value : '';
            _titleInput = titleContainer.selectAll('input.wiki-title').data([0]);
            _titleInput = _titleInput.enter().append('input').attr('type', 'text').attr('class', 'wiki-title').attr('id', field.domId).call(utilNoAuto).call(titleCombo).merge(_titleInput);
 
-           _titleInput.on('blur', blur).on('change', change);
+           _titleInput.on('blur', function () {
+             change(true);
+           }).on('change', function () {
+             change(false);
+           });
 
            var link = titleContainer.selectAll('.wiki-link').data([0]);
            link = link.enter().append('button').attr('class', 'form-field-button wiki-link').attr('title', _t('icons.view_on', {
            change(true);
          }
 
-         function blur() {
-           change(true);
-         }
-
          function change(skipWikidata) {
            var value = utilGetSetValue(_titleInput);
            var m = value.match(/https?:\/\/([-a-z]+)\.wikipedia\.org\/(?:wiki|\1-[-a-z]+)\/([^#]+)(?:#(.+))?/);
            });
          }).disclosureContent(renderDisclosureContent);
          var taginfo = services.taginfo;
-         var nearbyCombo = uiCombobox(context, 'parent-relation').minItems(1).fetcher(fetchNearbyRelations).itemsMouseEnter(function (d) {
+         var nearbyCombo = uiCombobox(context, 'parent-relation').minItems(1).fetcher(fetchNearbyRelations).itemsMouseEnter(function (d3_event, d) {
            if (d.relation) utilHighlightEntities([d.relation.id], true, context);
-         }).itemsMouseLeave(function (d) {
+         }).itemsMouseLeave(function (d3_event, d) {
            if (d.relation) utilHighlightEntities([d.relation.id], false, context);
          });
          var _inChange = false;
          }
 
          function deselectEntity(d3_event, entity) {
-           d3_event.stopPropagation();
-
            var selectedIDs = _selectedIDs.slice();
 
            var index = selectedIDs.indexOf(entity.id);
 
          function renderDisclosureContent(selection) {
            var list = selection.selectAll('.feature-list').data([0]);
-           list = list.enter().append('div').attr('class', 'feature-list').merge(list);
+           list = list.enter().append('ul').attr('class', 'feature-list').merge(list);
 
            var entities = _selectedIDs.map(function (id) {
              return context.hasEntity(id);
            var items = list.selectAll('.feature-list-item').data(entities, osmEntity.key);
            items.exit().remove(); // Enter
 
-           var enter = items.enter().append('button').attr('class', 'feature-list-item').on('click', selectEntity);
-           enter.each(function (d) {
+           var enter = items.enter().append('li').attr('class', 'feature-list-item').each(function (d) {
              select(this).on('mouseover', function () {
                utilHighlightEntities([d.id], true, context);
-             });
-             select(this).on('mouseout', function () {
+             }).on('mouseout', function () {
                utilHighlightEntities([d.id], false, context);
              });
            });
-           var label = enter.append('div').attr('class', 'label');
-           enter.append('button').attr('class', 'close').attr('title', _t('icons.deselect')).on('click', deselectEntity).call(svgIcon('#iD-icon-close'));
+           var label = enter.append('button').attr('class', 'label').on('click', selectEntity);
            label.append('span').attr('class', 'entity-geom-icon').call(svgIcon('', 'pre-text'));
            label.append('span').attr('class', 'entity-type');
-           label.append('span').attr('class', 'entity-name'); // Update
+           label.append('span').attr('class', 'entity-name');
+           enter.append('button').attr('class', 'close').attr('title', _t('icons.deselect')).on('click', deselectEntity).call(svgIcon('#iD-icon-close')); // Update
 
            items = items.merge(enter);
            items.selectAll('.entity-geom-icon use').attr('href', function () {
          };
 
          entityEditor.entityIDs = function (val) {
-           if (!arguments.length) return _entityIDs;
-           if (val && _entityIDs && utilArrayIdentical(_entityIDs, val)) return entityEditor; // exit early if no change
+           if (!arguments.length) return _entityIDs; // always reload these even if the entityIDs are unchanged, since we
+           // could be reselecting after something like dragging a node
 
-           _entityIDs = val;
            _base = context.graph();
            _coalesceChanges = false;
+           if (val && _entityIDs && utilArrayIdentical(_entityIDs, val)) return entityEditor; // exit early if no change
+
+           _entityIDs = val;
            loadActivePresets(true);
            return entityEditor.modified(false);
          };
            var inspectorWrap = selection.append('div').attr('class', 'inspector-hidden inspector-wrap');
 
            var hoverModeSelect = function hoverModeSelect(targets) {
-             context.container().selectAll('.feature-list-item').classed('hover', false);
+             context.container().selectAll('.feature-list-item button').classed('hover', false);
 
              if (context.selectedIDs().length > 1 && targets && targets.length) {
-               var elements = context.container().selectAll('.feature-list-item').filter(function (node) {
+               var elements = context.container().selectAll('.feature-list-item button').filter(function (node) {
                  return targets.indexOf(node) !== -1;
                });
 
                endMargin = 0;
              }
 
+             if (!isCollapsing) {
+               // unhide the sidebar's content before it transitions onscreen
+               selection.classed('collapsed', isCollapsing);
+             }
+
              selection.transition().style(xMarginProperty, endMargin + 'px').tween('panner', function () {
                var i = d3_interpolateNumber(startMargin, endMargin);
                return function (t) {
                  context.ui().onResize(moveMap ? undefined : [dx * scaleX, 0]);
                };
              }).on('end', function () {
-               selection.classed('collapsed', isCollapsing); // switch back from px to %
+               if (isCollapsing) {
+                 // hide the sidebar's content after it transitions offscreen
+                 selection.classed('collapsed', isCollapsing);
+               } // switch back from px to %
+
 
                if (!isCollapsing) {
                  var containerWidth = container.node().getBoundingClientRect().width;
            selection.append('a').attr('target', '_blank').attr('href', 'https://github.com/openstreetmap/iD').html(currVersion); // only show new version indicator to users that have used iD before
 
            if (isNewVersion && !isNewUser) {
-             selection.append('div').attr('class', 'badge').append('a').attr('target', '_blank').attr('href', 'https://github.com/openstreetmap/iD/blob/release/CHANGELOG.md#whats-new').call(svgIcon('#maki-gift-11')).call(uiTooltip().title(_t.html('version.whats_new', {
+             selection.append('a').attr('class', 'badge').attr('target', '_blank').attr('href', 'https://github.com/openstreetmap/iD/blob/release/CHANGELOG.md#whats-new').call(svgIcon('#maki-gift-11')).call(uiTooltip().title(_t.html('version.whats_new', {
                version: currVersion
-             })).placement('top'));
+             })).placement('top').scrollContainer(context.container().select('.main-footer-wrap')));
            }
          };
        }
            });
            enter.filter(function (d) {
              return d.id === 'custom';
-           }).append('button').attr('class', 'layer-browse').call(uiTooltip().title(_t.html('settings.custom_background.tooltip')).placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left')).on('click', editCustom).call(svgIcon('#iD-icon-more'));
+           }).append('button').attr('class', 'layer-browse').call(uiTooltip().title(_t.html('settings.custom_background.tooltip')).placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left')).on('click', function (d3_event) {
+             d3_event.preventDefault();
+             editCustom();
+           }).call(svgIcon('#iD-icon-more'));
            enter.filter(function (d) {
              return d.best();
            }).append('div').attr('class', 'best').call(uiTooltip().title(_t.html('background.best_imagery')).placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left')).append('span').html('&#9733;');
            }
          }
 
-         function editCustom(d3_event) {
-           d3_event.preventDefault();
+         function editCustom() {
            context.container().call(_settingsCustomBackground);
          }
 
 
            var itemsEnter = items.enter().append('li').attr('class', function (d) {
              return 'issue severity-' + d.severity;
-           }).on('click', function (d3_event, d) {
+           });
+           var labelsEnter = itemsEnter.append('button').attr('class', 'issue-label').on('click', function (d3_event, d) {
              context.validator().focusIssue(d);
            }).on('mouseover', function (d3_event, d) {
              utilHighlightEntities(d.entityIds, true, context);
            }).on('mouseout', function (d3_event, d) {
              utilHighlightEntities(d.entityIds, false, context);
            });
-           var labelsEnter = itemsEnter.append('div').attr('class', 'issue-label');
            var textEnter = labelsEnter.append('span').attr('class', 'issue-text');
            textEnter.append('span').attr('class', 'issue-icon').each(function (d) {
              var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error');
              toggleLayer('data');
            });
            labelEnter.append('span').html(_t.html('map_data.layers.custom.title'));
-           liEnter.append('button').attr('class', 'open-data-options').call(uiTooltip().title(_t.html('settings.custom_data.tooltip')).placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left')).on('click', editCustom).call(svgIcon('#iD-icon-more'));
+           liEnter.append('button').attr('class', 'open-data-options').call(uiTooltip().title(_t.html('settings.custom_data.tooltip')).placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left')).on('click', function (d3_event) {
+             d3_event.preventDefault();
+             editCustom();
+           }).call(svgIcon('#iD-icon-more'));
            liEnter.append('button').attr('class', 'zoom-to-data').call(uiTooltip().title(_t.html('map_data.layers.custom.zoom')).placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left')).on('click', function (d3_event) {
              if (select(this).classed('disabled')) return;
              d3_event.preventDefault();
            ul.selectAll('button.zoom-to-data').classed('disabled', !hasData);
          }
 
-         function editCustom(d3_event) {
-           d3_event.preventDefault();
+         function editCustom() {
            context.container().call(settingsCustomData);
          }
 
            // child to trick Safari into not showing the selection UI.
 
            overMap.append('div').attr('class', 'select-trap').text('t');
-           overMap.append('div').attr('class', 'spinner').call(uiSpinner(context));
-           overMap.append('div').attr('class', 'attribution-wrap').attr('dir', 'ltr').call(uiAttribution(context)); // Map controls
+           overMap.call(uiMapInMap(context)).call(uiNotice(context));
+           overMap.append('div').attr('class', 'spinner').call(uiSpinner(context)); // Map controls
 
            var controls = overMap.append('div').attr('class', 'map-controls');
            controls.append('div').attr('class', 'map-control zoombuttons').call(uiZoom(context));
              controls.append('div').attr('class', 'map-control map-pane-control ' + pane.id + '-control').call(pane.renderToggleButton);
              panes.call(pane.renderPane);
            });
-           ui.info = uiInfo(context); // Add absolutely-positioned elements that sit on top of the map
-           // This should happen after the map is ready (center/zoom)
-
-           overMap.call(uiMapInMap(context)).call(ui.info).call(uiNotice(context));
+           ui.info = uiInfo(context);
+           overMap.call(ui.info);
            overMap.append('div').attr('class', 'photoviewer').classed('al', true) // 'al'=left,  'ar'=right
-           .classed('hide', true).call(ui.photoviewer); // Add footer
+           .classed('hide', true).call(ui.photoviewer);
+           overMap.append('div').attr('class', 'attribution-wrap').attr('dir', 'ltr').call(uiAttribution(context)); // Add footer
 
            var about = content.append('div').attr('class', 'map-footer');
            about.append('div').attr('class', 'api-status').call(uiStatus(context));
              delete _needWidth[selector];
            }
 
-           var element = select(selector);
-           var scrollWidth = element.property('scrollWidth');
-           var clientWidth = element.property('clientWidth');
+           var selection = context.container().select(selector);
+           if (selection.empty()) return;
+           var scrollWidth = selection.property('scrollWidth');
+           var clientWidth = selection.property('clientWidth');
            var needed = _needWidth[selector] || scrollWidth;
 
            if (scrollWidth > clientWidth) {
              // overflow happening
-             element.classed('narrow', true);
+             selection.classed('narrow', true);
 
              if (!_needWidth[selector]) {
                _needWidth[selector] = scrollWidth;
              }
            } else if (scrollWidth >= needed) {
-             element.classed('narrow', false);
+             selection.classed('narrow', false);
            }
          };
 
 
          var _deferred = new Set();
 
-         context.version = '2.19.1';
+         context.version = '2.19.5';
          context.privacyVersion = '20200407'; // iD will alter the hash so cache the parameters intended to setup the session
 
          context.initialHashParams = window.location.hash ? utilStringQs(window.location.hash) : {};
          };
 
          context.zoomToEntity = function (entityID, zoomTo) {
-           if (zoomTo !== false) {
-             context.loadEntity(entityID, function (err, result) {
-               if (err) return;
+           // be sure to load the entity even if we're not going to zoom to it
+           context.loadEntity(entityID, function (err, result) {
+             if (err) return;
+
+             if (zoomTo !== false) {
                var entity = result.data.find(function (e) {
                  return e.id === entityID;
                });
                if (entity) {
                  _map.zoomTo(entity);
                }
-             });
-           }
+             }
+           });
 
            _map.on('drawn.zoomToEntity', function () {
              if (!context.hasEntity(entityID)) return;